AX: richer text change notifications (142719)
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Apr 2015 16:49:47 +0000 (16:49 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Apr 2015 16:49:47 +0000 (16:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142719

Patch by Doug Russell <d_russell@apple.com> on 2015-04-24
Reviewed by Darin Adler.

Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.

Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.

Source/WebCore:

Tests: platform/mac/accessibility/input-replacevalue-userinfo.html
       platform/mac/accessibility/selection-change-userinfo.html
       platform/mac/accessibility/value-change-userinfo.html

* CMakeLists.txt:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::AXObjectCache):
(WebCore::AXObjectCache::notificationPostTimerFired):
(WebCore::AXObjectCache::passwordNotificationPostTimerFired):
(WebCore::AXObjectCache::showIntent):
(WebCore::AXObjectCache::setTextSelectionIntent):
(WebCore::isPasswordFieldOrContainedByPasswordField):
(WebCore::AXObjectCache::postTextStateChangeNotification):
(WebCore::AXObjectCache::postTextReplacementNotification):
(WebCore::AXObjectCache::enqueuePasswordValueChangeNotification):
(WebCore::AXObjectCache::rootWebArea):
(WebCore::AXObjectCache::selectedChildrenChanged): Deleted.
* accessibility/AXObjectCache.h:
(WebCore::AXObjectCache::postTextStateChangeNotification):
(WebCore::AXObjectCache::postTextReplacementNotification):
(WebCore::AXObjectCache::postTextStateChangePlatformNotification):
(WebCore::AXObjectCache::postTextReplacementPlatformNotification):
(WebCore::AXObjectCache::textChangeForEditType):
(WebCore::AXObjectCache::nodeTextChangePlatformNotification):
(WebCore::AXObjectCache::computedObjectAttributeCache): Deleted.
(WebCore::AXObjectCache::getOrCreate): Deleted.
(WebCore::AXObjectCache::attachWrapper): Deleted.
* accessibility/AXTextStateChangeIntent.h: Added.
(WebCore::AXTextStateChangeIntent::AXTextStateChangeIntent):
* accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::passwordFieldOrContainingPasswordField):
* accessibility/AccessibilityNodeObject.h:
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::isContainedByPasswordField):
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::passwordFieldOrContainingPasswordField):
(WebCore::AccessibilityObject::isPasswordField): Deleted.
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::setSelectedTextRange):
(WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange):
* accessibility/AccessibilityScrollView.h:
* accessibility/atk/AXObjectCacheAtk.cpp:
(WebCore::AXObjectCache::textChangeForEditType):
(WebCore::AXObjectCache::nodeTextChangePlatformNotification):
(WebCore::AXObjectCache::postPlatformNotification): Deleted.
* accessibility/ios/AXObjectCacheIOS.mm:
(WebCore::AXObjectCache::postTextStateChangePlatformNotification):
(WebCore::AXObjectCache::postTextReplacementPlatformNotification):
* accessibility/mac/AXObjectCacheMac.mm:
(WebCore::AXObjectCache::setShouldRepostNotificationsForTests):
(WebCore::AXPostNotificationWithUserInfo):
(WebCore::AXObjectCache::postPlatformNotification):
(WebCore::AXObjectCache::postTextStateChangePlatformNotification):
(WebCore::textReplacementChangeDictionary):
(WebCore::AXObjectCache::postTextReplacementPlatformNotification):
* accessibility/mac/WebAccessibilityObjectWrapperBase.h:
* accessibility/mac/WebAccessibilityObjectWrapperBase.mm:
(+[WebAccessibilityObjectWrapperBase accessibilitySetShouldRepostNotifications:]):
(-[WebAccessibilityObjectWrapperBase accessibilityPostedNotification:]):
(arrayRemovingNonJSONTypes):
(dictionaryRemovingNonJSONTypes):
(-[WebAccessibilityObjectWrapperBase accessibilityPostedNotification:userInfo:]):
* accessibility/mac/WebAccessibilityObjectWrapperMac.h:
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(textMarkerRangeFromVisiblePositions):
(-[WebAccessibilityObjectWrapper textMarkerRangeFromVisiblePositions:endPosition:]):
* editing/AppendNodeCommand.cpp:
(WebCore::AppendNodeCommand::AppendNodeCommand):
(WebCore::sendAXTextChangedIgnoringLineBreaks):
(WebCore::AppendNodeCommand::doApply):
(WebCore::AppendNodeCommand::doUnapply):
* editing/AppendNodeCommand.h:
(WebCore::AppendNodeCommand::create):
* editing/ApplyStyleCommand.cpp:
(WebCore::ApplyStyleCommand::ApplyStyleCommand):
(WebCore::ApplyStyleCommand::applyBlockStyle): Deleted.
* editing/ApplyStyleCommand.h:
* editing/CompositeEditCommand.cpp:
(WebCore::EditCommandComposition::unapplyEditType):
(WebCore::CompositeEditCommand::CompositeEditCommand):
(WebCore::CompositeEditCommand::apply):
(WebCore::CompositeEditCommand::insertParagraphSeparator):
(WebCore::CompositeEditCommand::insertNodeBefore):
(WebCore::CompositeEditCommand::appendNode):
(WebCore::CompositeEditCommand::removeNodePreservingChildren):
(WebCore::CompositeEditCommand::insertTextIntoNode):
(WebCore::CompositeEditCommand::deleteTextFromNode):
(WebCore::CompositeEditCommand::replaceTextInNode):
(WebCore::CompositeEditCommand::moveParagraphs):
(WebCore::EditCommandComposition::getNodesInCommand): Deleted.
(WebCore::CompositeEditCommand::applyStyle): Deleted.
(WebCore::CompositeEditCommand::insertLineBreak): Deleted.
(WebCore::CompositeEditCommand::insertNodeAt): Deleted.
(WebCore::CompositeEditCommand::removeChildrenInRange): Deleted.
(WebCore::CompositeEditCommand::inputText): Deleted.
* editing/CompositeEditCommand.h:
* editing/DeleteFromTextNodeCommand.cpp:
(WebCore::DeleteFromTextNodeCommand::DeleteFromTextNodeCommand):
(WebCore::DeleteFromTextNodeCommand::doApply):
(WebCore::DeleteFromTextNodeCommand::doUnapply):
* editing/DeleteFromTextNodeCommand.h:
(WebCore::DeleteFromTextNodeCommand::create):
(WebCore::DeleteFromTextNodeCommand::deletedText):
* editing/DeleteSelectionCommand.cpp:
(WebCore::DeleteSelectionCommand::DeleteSelectionCommand):
(WebCore::DeleteSelectionCommand::preservesTypingStyle): Deleted.
* editing/DeleteSelectionCommand.h:
(WebCore::DeleteSelectionCommand::create):
* editing/DictationCommand.cpp:
(WebCore::DictationCommand::insertTextRunWithoutNewlines):
(WebCore::DictationCommand::insertParagraphSeparator):
* editing/EditAction.h:
* editing/EditCommand.cpp:
(WebCore::EditCommand::EditCommand):
(WebCore::EditCommand::editingAction):
(WebCore::EditCommand::applyEditType):
(WebCore::EditCommand::unapplyEditType):
(WebCore::SimpleEditCommand::SimpleEditCommand):
(WebCore::SimpleEditCommand::notifyAccessibilityForTextChange):
(WebCore::EditCommand::setParent): Deleted.
* editing/EditCommand.h:
* editing/EditingAllInOne.cpp:
* editing/Editor.cpp:
(WebCore::Editor::handleTextEvent):
(WebCore::Editor::deleteSelectionWithSmartDelete):
(WebCore::Editor::replaceSelectionWithFragment):
(WebCore::Editor::replaceSelectionWithText):
(WebCore::Editor::appliedEditing):
(WebCore::Editor::unappliedEditing):
(WebCore::Editor::performCutOrCopy):
(WebCore::Editor::markMisspellingsAfterTypingToWord):
(WebCore::Editor::changeBackToReplacedString):
(WebCore::Editor::transpose):
(WebCore::Editor::changeSelectionAfterCommand):
* editing/Editor.h:
* editing/EditorCommand.cpp:
(WebCore::executeInsertFragment):
* editing/FrameSelection.cpp:
(WebCore::FrameSelection::moveTo):
(WebCore::FrameSelection::moveWithoutValidationTo):
(WebCore::FrameSelection::setSelectionByMouseIfDifferent):
(WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance):
(WebCore::FrameSelection::setSelection):
(WebCore::FrameSelection::updateAndRevealSelection):
(WebCore::isBoundary):
(WebCore::FrameSelection::textSelectionIntent):
(WebCore::FrameSelection::modify):
(WebCore::FrameSelection::selectAll):
(WebCore::FrameSelection::wordSelectionContainingCaretSelection):
(WebCore::FrameSelection::modifyMovingBackward): Deleted.
(WebCore::FrameSelection::selectFrameElementInParentIfFullySelected): Deleted.
(WebCore::FrameSelection::selectionAtWordStart): Deleted.
* editing/FrameSelection.h:
(WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
(WebCore::FrameSelection::selection): Deleted.
* editing/InsertIntoTextNodeCommand.cpp:
(WebCore::InsertIntoTextNodeCommand::InsertIntoTextNodeCommand):
(WebCore::InsertIntoTextNodeCommand::doApply):
(WebCore::InsertIntoTextNodeCommand::doUnapply):
* editing/InsertIntoTextNodeCommand.h:
(WebCore::InsertIntoTextNodeCommand::create):
(WebCore::InsertIntoTextNodeCommand::insertedText):
* editing/InsertNodeBeforeCommand.cpp:
(WebCore::InsertNodeBeforeCommand::InsertNodeBeforeCommand):
(WebCore::InsertNodeBeforeCommand::doApply):
(WebCore::InsertNodeBeforeCommand::doUnapply):
* editing/InsertNodeBeforeCommand.h:
(WebCore::InsertNodeBeforeCommand::create):
* editing/InsertParagraphSeparatorCommand.cpp:
(WebCore::InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand):
* editing/InsertParagraphSeparatorCommand.h:
(WebCore::InsertParagraphSeparatorCommand::create):
* editing/InsertTextCommand.cpp:
(WebCore::InsertTextCommand::InsertTextCommand):
* editing/InsertTextCommand.h:
(WebCore::InsertTextCommand::create):
(WebCore::InsertTextCommand::createWithMarkerSupplier):
* editing/MoveSelectionCommand.cpp:
(WebCore::MoveSelectionCommand::doApply):
* editing/RemoveNodePreservingChildrenCommand.cpp:
(WebCore::RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand):
* editing/RemoveNodePreservingChildrenCommand.h:
(WebCore::RemoveNodePreservingChildrenCommand::create):
* editing/ReplaceDeleteFromTextNodeCommand.cpp: Copied from Source/WebCore/editing/AppendNodeCommand.h.
(WebCore::ReplaceDeleteFromTextNodeCommand::ReplaceDeleteFromTextNodeCommand):
(WebCore::ReplaceDeleteFromTextNodeCommand::notifyAccessibilityForTextChange):
* editing/ReplaceDeleteFromTextNodeCommand.h: Copied from Source/WebCore/editing/AppendNodeCommand.h.
* editing/ReplaceInsertIntoTextNodeCommand.cpp: Added.
(WebCore::ReplaceInsertIntoTextNodeCommand::ReplaceInsertIntoTextNodeCommand):
(WebCore::ReplaceInsertIntoTextNodeCommand::notifyAccessibilityForTextChange):
* editing/ReplaceInsertIntoTextNodeCommand.h: Copied from Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h.
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplaceSelectionCommand::ReplaceSelectionCommand):
(WebCore::ReplaceSelectionCommand::InsertedNodes::didReplaceNode): Deleted.
(WebCore::ReplaceSelectionCommand::insertAsListItems): Deleted.
* editing/ReplaceSelectionCommand.h:
(WebCore::ReplaceSelectionCommand::create):
* editing/TypingCommand.cpp:
(WebCore::TypingCommand::insertTextRunWithoutNewlines):
(WebCore::TypingCommand::insertParagraphSeparator):
* editing/atk/FrameSelectionAtk.cpp:
(WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
* editing/ios/DictationCommandIOS.cpp:
(WebCore::DictationCommandIOS::DictationCommandIOS):
* editing/ios/DictationCommandIOS.h:
* editing/mac/FrameSelectionMac.mm:
(WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
* html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::setInnerTextValue):
* page/DragController.cpp:
(WebCore::DragController::concludeEditDrag):
* page/EventHandler.cpp:
(WebCore::setInitialKeyboardSelection):
* page/FocusController.cpp:
(WebCore::FocusController::advanceFocusInDocumentOrder):

Source/WebKit/mac:

* WebCoreSupport/WebEditorClient.mm:
(undoNameForEditAction):

Source/WebKit2:

* UIProcess/WebEditCommandProxy.cpp:
(WebKit::WebEditCommandProxy::nameForEditAction):

Tools:

* DumpRenderTree/mac/AccessibilityNotificationHandler.h:
* DumpRenderTree/mac/AccessibilityNotificationHandler.mm:
(-[AccessibilityNotificationHandler stopObserving]):
(-[AccessibilityNotificationHandler _notificationReceived:]):
* DumpRenderTree/mac/AccessibilityUIElementMac.mm:
(AccessibilityUIElement::removeNotificationListener):
* WebKitTestRunner/InjectedBundle/mac/AccessibilityNotificationHandler.mm:
(-[AccessibilityNotificationHandler _notificationReceived:]):
* WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::removeNotificationListener):

LayoutTests:

* platform/mac/accessibility/input-replacevalue-userinfo-expected.txt: Added.
* platform/mac/accessibility/input-replacevalue-userinfo.html: Added.
* platform/mac/accessibility/selection-change-userinfo-expected.txt: Added.
* platform/mac/accessibility/selection-change-userinfo.html: Added.
* platform/mac/accessibility/value-change-userinfo-expected.txt: Added.
* platform/mac/accessibility/value-change-userinfo.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183266 268f45cc-cd09-0410-ab3c-d52691b4dbfc

84 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo.html [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/selection-change-userinfo-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/selection-change-userinfo.html [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/value-change-userinfo-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/value-change-userinfo.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AXTextStateChangeIntent.h [new file with mode: 0644]
Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Source/WebCore/accessibility/AccessibilityNodeObject.h
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/accessibility/AccessibilityScrollView.h
Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm
Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.mm
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
Source/WebCore/editing/AppendNodeCommand.cpp
Source/WebCore/editing/AppendNodeCommand.h
Source/WebCore/editing/ApplyStyleCommand.cpp
Source/WebCore/editing/ApplyStyleCommand.h
Source/WebCore/editing/CompositeEditCommand.cpp
Source/WebCore/editing/CompositeEditCommand.h
Source/WebCore/editing/DeleteFromTextNodeCommand.cpp
Source/WebCore/editing/DeleteFromTextNodeCommand.h
Source/WebCore/editing/DeleteSelectionCommand.cpp
Source/WebCore/editing/DeleteSelectionCommand.h
Source/WebCore/editing/DictationCommand.cpp
Source/WebCore/editing/EditAction.h
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/EditorCommand.cpp
Source/WebCore/editing/FrameSelection.cpp
Source/WebCore/editing/FrameSelection.h
Source/WebCore/editing/InsertIntoTextNodeCommand.cpp
Source/WebCore/editing/InsertIntoTextNodeCommand.h
Source/WebCore/editing/InsertNodeBeforeCommand.cpp
Source/WebCore/editing/InsertNodeBeforeCommand.h
Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
Source/WebCore/editing/InsertParagraphSeparatorCommand.h
Source/WebCore/editing/InsertTextCommand.cpp
Source/WebCore/editing/InsertTextCommand.h
Source/WebCore/editing/MoveSelectionCommand.cpp
Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp
Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h
Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp [new file with mode: 0644]
Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.h [new file with mode: 0644]
Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.cpp [new file with mode: 0644]
Source/WebCore/editing/ReplaceInsertIntoTextNodeCommand.h [new file with mode: 0644]
Source/WebCore/editing/ReplaceSelectionCommand.cpp
Source/WebCore/editing/ReplaceSelectionCommand.h
Source/WebCore/editing/TypingCommand.cpp
Source/WebCore/editing/atk/FrameSelectionAtk.cpp
Source/WebCore/editing/ios/DictationCommandIOS.cpp
Source/WebCore/editing/ios/DictationCommandIOS.h
Source/WebCore/editing/mac/FrameSelectionMac.mm
Source/WebCore/html/HTMLTextFormControlElement.cpp
Source/WebCore/page/DragController.cpp
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/FocusController.cpp
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebCoreSupport/WebEditorClient.mm
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/WebEditCommandProxy.cpp
Tools/ChangeLog
Tools/DumpRenderTree/mac/AccessibilityNotificationHandler.h
Tools/DumpRenderTree/mac/AccessibilityNotificationHandler.mm
Tools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityNotificationHandler.mm
Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm

index ee3d29c..0a97ae4 100644 (file)
@@ -1,3 +1,21 @@
+2015-04-24  Doug Russell  <d_russell@apple.com>
+
+        AX: richer text change notifications (142719)
+        https://bugs.webkit.org/show_bug.cgi?id=142719
+
+        Reviewed by Darin Adler.
+
+        Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.
+
+        Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.
+
+        * platform/mac/accessibility/input-replacevalue-userinfo-expected.txt: Added.
+        * platform/mac/accessibility/input-replacevalue-userinfo.html: Added.
+        * platform/mac/accessibility/selection-change-userinfo-expected.txt: Added.
+        * platform/mac/accessibility/selection-change-userinfo.html: Added.
+        * platform/mac/accessibility/value-change-userinfo-expected.txt: Added.
+        * platform/mac/accessibility/value-change-userinfo.html: Added.
+
 2015-04-24  Antti Koivisto  <antti@apple.com>
 
         Memory cache live resources repeatedly purged during painting
diff --git a/LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo-expected.txt b/LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo-expected.txt
new file mode 100644 (file)
index 0000000..efa6f82
--- /dev/null
@@ -0,0 +1,20 @@
+  
+This tests value change notifications user info data when replacing the contents of an input field.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS addedNotification is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
+PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is "0"
+PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeDelete
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
+PASS results[resultIndex]["AXTextChangeValues"][1]["AXTextChangeValue"] is "1"
+PASS results[resultIndex]["AXTextChangeValues"][1]["AXTextEditType"] is AXTextEditTypeInsert
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeEdit
+PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextChangeValue"] is " "
+PASS results[resultIndex]["AXTextChangeValues"][0]["AXTextEditType"] is AXTextEditTypeInsert
+
diff --git a/LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo.html b/LayoutTests/platform/mac/accessibility/input-replacevalue-userinfo.html
new file mode 100644 (file)
index 0000000..de0de31
--- /dev/null
@@ -0,0 +1,83 @@
+<!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">
+
+    <input type="password" id="securevaluetest">
+
+    <input type="text" id="valuetest">
+
+    <p id="description"></p>
+    <div id="console"></div>
+    <div id="notifications"></div>
+
+    <script>
+
+        description("This tests value change notifications user info data when replacing the contents of an input field.");
+
+        var AXTextStateChangeTypeEdit = 1;
+
+        var AXTextEditTypeDelete = 1;
+        var AXTextEditTypeInsert = AXTextEditTypeDelete + 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 == 2) {
+
+                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\"0\"");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeDelete");
+                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][1][\"AXTextChangeValue\"]", "\"1\"");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][1][\"AXTextEditType\"]", "AXTextEditTypeInsert");
+
+                    // Password notifications will be insert only and the value string will be whitespace
+                    resultIndex++;
+                    shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeEdit");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextChangeValue\"]", "\" \"");
+                    shouldBe("results[resultIndex][\"AXTextChangeValues\"][0][\"AXTextEditType\"]", "AXTextEditTypeInsert");
+
+                    webArea.removeNotificationListener();
+                    window.testRunner.notifyDone();
+                }
+            }
+        }
+
+        function setvalue() {
+            document.getElementById("valuetest").value = "1";
+        }
+
+        function setsecurevalue() {
+            document.getElementById("securevaluetest").value = "1";
+        }
+
+        document.getElementById("valuetest").value = "0";
+        document.getElementById("securevaluetest").value = "0";
+
+        if (window.accessibilityController) {
+             window.testRunner.waitUntilDone();
+
+            accessibilityController.enableEnhancedAccessibility(true);
+
+            webArea = accessibilityController.rootElement.childAtIndex(0);
+            var addedNotification = webArea.addNotificationListener(notificationCallback);
+            shouldBe("addedNotification", "true");
+
+            setvalue();
+
+            setsecurevalue();
+        }
+    </script>
+
+    <script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/platform/mac/accessibility/selection-change-userinfo-expected.txt b/LayoutTests/platform/mac/accessibility/selection-change-userinfo-expected.txt
new file mode 100644 (file)
index 0000000..dd22ffd
--- /dev/null
@@ -0,0 +1,95 @@
+one two three. four five six.
+
+seven eight nine.
+
+This tests selection 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 AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityCharacter
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityCharacter
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionPrevious
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityWord
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityWord
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionPrevious
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionPrevious
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionPrevious
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionPrevious
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionBeginning
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionBeginning
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionBeginning
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityDocument
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionMove
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityDocument
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionBeginning
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityCharacter
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityWord
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionNext
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityLine
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularitySentence
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityParagraph
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS results[resultIndex]["AXTextStateChangeType"] is AXTextStateChangeTypeSelectionExtend
+PASS results[resultIndex]["AXTextSelectionGranularity"] is AXTextSelectionGranularityDocument
+PASS results[resultIndex]["AXTextSelectionDirection"] is AXTextSelectionDirectionEnd
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac/accessibility/selection-change-userinfo.html b/LayoutTests/platform/mac/accessibility/selection-change-userinfo.html
new file mode 100644 (file)
index 0000000..257537a
--- /dev/null
@@ -0,0 +1,217 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body id="body">
+
+<div role="textbox" tabindex=0 id="textbox" contenteditable=true>
+<p>one two three. four five six.</p>
+<p>seven eight nine.</p>
+</div>
+
+<p id="description"></p>
+<div id="console"></div>
+<div id="notifications"></div>
+
+<script>
+
+    description("This tests selection change notifications user info data.");
+
+    var AXTextStateChangeTypeSelectionMove = 2;
+    var AXTextStateChangeTypeSelectionExtend = AXTextStateChangeTypeSelectionMove + 1;
+
+    var AXTextSelectionDirectionBeginning = 1;
+    var AXTextSelectionDirectionEnd = AXTextSelectionDirectionBeginning + 1;
+    var AXTextSelectionDirectionPrevious = AXTextSelectionDirectionEnd + 1;
+    var AXTextSelectionDirectionNext = AXTextSelectionDirectionPrevious + 1;
+
+    var AXTextSelectionGranularityCharacter = 1;
+    var AXTextSelectionGranularityWord = AXTextSelectionGranularityCharacter + 1;
+    var AXTextSelectionGranularityLine = AXTextSelectionGranularityWord + 1;
+    var AXTextSelectionGranularitySentence = AXTextSelectionGranularityLine + 1;
+    var AXTextSelectionGranularityParagraph = AXTextSelectionGranularitySentence + 1;
+    var AXTextSelectionGranularityDocument = AXTextSelectionGranularityParagraph + 2;
+
+    var gran = ["character", "word", "sentence", "line", "paragraph", "lineboundary", "sentenceboundary", "paragraphboundary", "documentboundary"];
+    var webArea = 0;
+    var count = 0;
+    var results = [];
+    var resultIndex = 0;
+    function notificationCallback(notification, userInfo) {
+        if (notification == "AXSelectedTextChanged") {
+            count++;
+            if (userInfo)
+                results.push(userInfo);
+            if (count == gran.length * 4) {
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityCharacter");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityCharacter");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionPrevious");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityWord");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityWord");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionPrevious");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionPrevious");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionPrevious");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionPrevious");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionBeginning");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionBeginning");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionBeginning");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityDocument");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionMove");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityDocument");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionBeginning");
+
+                resultIndex++;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityCharacter");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityWord");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionNext");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityLine");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularitySentence");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityParagraph");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                resultIndex += 2;
+                shouldBe("results[resultIndex][\"AXTextStateChangeType\"]", "AXTextStateChangeTypeSelectionExtend");
+                shouldBe("results[resultIndex][\"AXTextSelectionGranularity\"]", "AXTextSelectionGranularityDocument");
+                shouldBe("results[resultIndex][\"AXTextSelectionDirection\"]", "AXTextSelectionDirectionEnd");
+
+                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");
+
+        textbox = document.getElementById("textbox");
+        textbox.focus();
+
+        // Trigger selection changes.
+        var s = window.getSelection();
+        s.setPosition(textbox, 0);
+        for (var i in gran) {
+            s.modify("move", "forward", gran[i]);
+            s.modify("move", "backward", gran[i]);
+        }
+        for (var i in gran) {
+            s.setPosition(textbox, 0);
+            s.modify("extend", "forward", gran[i]);
+        }
+    }
+
+</script>
+
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/platform/mac/accessibility/value-change-userinfo-expected.txt b/LayoutTests/platform/mac/accessibility/value-change-userinfo-expected.txt
new file mode 100644 (file)
index 0000000..393aabe
--- /dev/null
@@ -0,0 +1,29 @@
+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/platform/mac/accessibility/value-change-userinfo.html b/LayoutTests/platform/mac/accessibility/value-change-userinfo.html
new file mode 100644 (file)
index 0000000..3dad341
--- /dev/null
@@ -0,0 +1,97 @@
+<!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>
index c8e205a..e7c9791 100644 (file)
@@ -1513,6 +1513,8 @@ 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 b6e2e3b..87931a4 100644 (file)
@@ -1,3 +1,233 @@
+2015-04-24  Doug Russell  <d_russell@apple.com>
+
+        AX: richer text change notifications (142719)
+        https://bugs.webkit.org/show_bug.cgi?id=142719
+
+        Reviewed by Darin Adler.
+
+        Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.
+
+        Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.
+
+        Tests: platform/mac/accessibility/input-replacevalue-userinfo.html
+               platform/mac/accessibility/selection-change-userinfo.html
+               platform/mac/accessibility/value-change-userinfo.html
+
+        * CMakeLists.txt:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * WebCore.xcodeproj/project.pbxproj:
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::AXObjectCache):
+        (WebCore::AXObjectCache::notificationPostTimerFired):
+        (WebCore::AXObjectCache::passwordNotificationPostTimerFired):
+        (WebCore::AXObjectCache::showIntent):
+        (WebCore::AXObjectCache::setTextSelectionIntent):
+        (WebCore::isPasswordFieldOrContainedByPasswordField):
+        (WebCore::AXObjectCache::postTextStateChangeNotification):
+        (WebCore::AXObjectCache::postTextReplacementNotification):
+        (WebCore::AXObjectCache::enqueuePasswordValueChangeNotification):
+        (WebCore::AXObjectCache::rootWebArea):
+        (WebCore::AXObjectCache::selectedChildrenChanged): Deleted.
+        * accessibility/AXObjectCache.h:
+        (WebCore::AXObjectCache::postTextStateChangeNotification):
+        (WebCore::AXObjectCache::postTextReplacementNotification):
+        (WebCore::AXObjectCache::postTextStateChangePlatformNotification):
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotification):
+        (WebCore::AXObjectCache::textChangeForEditType):
+        (WebCore::AXObjectCache::nodeTextChangePlatformNotification):
+        (WebCore::AXObjectCache::computedObjectAttributeCache): Deleted.
+        (WebCore::AXObjectCache::getOrCreate): Deleted.
+        (WebCore::AXObjectCache::attachWrapper): Deleted.
+        * accessibility/AXTextStateChangeIntent.h: Added.
+        (WebCore::AXTextStateChangeIntent::AXTextStateChangeIntent):
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore::AccessibilityNodeObject::passwordFieldOrContainingPasswordField):
+        * accessibility/AccessibilityNodeObject.h:
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::isContainedByPasswordField):
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::passwordFieldOrContainingPasswordField):
+        (WebCore::AccessibilityObject::isPasswordField): Deleted.
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::setSelectedTextRange):
+        (WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange):
+        * accessibility/AccessibilityScrollView.h:
+        * accessibility/atk/AXObjectCacheAtk.cpp:
+        (WebCore::AXObjectCache::textChangeForEditType):
+        (WebCore::AXObjectCache::nodeTextChangePlatformNotification):
+        (WebCore::AXObjectCache::postPlatformNotification): Deleted.
+        * accessibility/ios/AXObjectCacheIOS.mm:
+        (WebCore::AXObjectCache::postTextStateChangePlatformNotification):
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotification):
+        * accessibility/mac/AXObjectCacheMac.mm:
+        (WebCore::AXObjectCache::setShouldRepostNotificationsForTests):
+        (WebCore::AXPostNotificationWithUserInfo):
+        (WebCore::AXObjectCache::postPlatformNotification):
+        (WebCore::AXObjectCache::postTextStateChangePlatformNotification):
+        (WebCore::textReplacementChangeDictionary):
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotification):
+        * accessibility/mac/WebAccessibilityObjectWrapperBase.h:
+        * accessibility/mac/WebAccessibilityObjectWrapperBase.mm:
+        (+[WebAccessibilityObjectWrapperBase accessibilitySetShouldRepostNotifications:]):
+        (-[WebAccessibilityObjectWrapperBase accessibilityPostedNotification:]):
+        (arrayRemovingNonJSONTypes):
+        (dictionaryRemovingNonJSONTypes):
+        (-[WebAccessibilityObjectWrapperBase accessibilityPostedNotification:userInfo:]):
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.h:
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (textMarkerRangeFromVisiblePositions):
+        (-[WebAccessibilityObjectWrapper textMarkerRangeFromVisiblePositions:endPosition:]):
+        * editing/AppendNodeCommand.cpp:
+        (WebCore::AppendNodeCommand::AppendNodeCommand):
+        (WebCore::sendAXTextChangedIgnoringLineBreaks):
+        (WebCore::AppendNodeCommand::doApply):
+        (WebCore::AppendNodeCommand::doUnapply):
+        * editing/AppendNodeCommand.h:
+        (WebCore::AppendNodeCommand::create):
+        * editing/ApplyStyleCommand.cpp:
+        (WebCore::ApplyStyleCommand::ApplyStyleCommand):
+        (WebCore::ApplyStyleCommand::applyBlockStyle): Deleted.
+        * editing/ApplyStyleCommand.h:
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::EditCommandComposition::unapplyEditType):
+        (WebCore::CompositeEditCommand::CompositeEditCommand):
+        (WebCore::CompositeEditCommand::apply):
+        (WebCore::CompositeEditCommand::insertParagraphSeparator):
+        (WebCore::CompositeEditCommand::insertNodeBefore):
+        (WebCore::CompositeEditCommand::appendNode):
+        (WebCore::CompositeEditCommand::removeNodePreservingChildren):
+        (WebCore::CompositeEditCommand::insertTextIntoNode):
+        (WebCore::CompositeEditCommand::deleteTextFromNode):
+        (WebCore::CompositeEditCommand::replaceTextInNode):
+        (WebCore::CompositeEditCommand::moveParagraphs):
+        (WebCore::EditCommandComposition::getNodesInCommand): Deleted.
+        (WebCore::CompositeEditCommand::applyStyle): Deleted.
+        (WebCore::CompositeEditCommand::insertLineBreak): Deleted.
+        (WebCore::CompositeEditCommand::insertNodeAt): Deleted.
+        (WebCore::CompositeEditCommand::removeChildrenInRange): Deleted.
+        (WebCore::CompositeEditCommand::inputText): Deleted.
+        * editing/CompositeEditCommand.h:
+        * editing/DeleteFromTextNodeCommand.cpp:
+        (WebCore::DeleteFromTextNodeCommand::DeleteFromTextNodeCommand):
+        (WebCore::DeleteFromTextNodeCommand::doApply):
+        (WebCore::DeleteFromTextNodeCommand::doUnapply):
+        * editing/DeleteFromTextNodeCommand.h:
+        (WebCore::DeleteFromTextNodeCommand::create):
+        (WebCore::DeleteFromTextNodeCommand::deletedText):
+        * editing/DeleteSelectionCommand.cpp:
+        (WebCore::DeleteSelectionCommand::DeleteSelectionCommand):
+        (WebCore::DeleteSelectionCommand::preservesTypingStyle): Deleted.
+        * editing/DeleteSelectionCommand.h:
+        (WebCore::DeleteSelectionCommand::create):
+        * editing/DictationCommand.cpp:
+        (WebCore::DictationCommand::insertTextRunWithoutNewlines):
+        (WebCore::DictationCommand::insertParagraphSeparator):
+        * editing/EditAction.h:
+        * editing/EditCommand.cpp:
+        (WebCore::EditCommand::EditCommand):
+        (WebCore::EditCommand::editingAction):
+        (WebCore::EditCommand::applyEditType):
+        (WebCore::EditCommand::unapplyEditType):
+        (WebCore::SimpleEditCommand::SimpleEditCommand):
+        (WebCore::SimpleEditCommand::notifyAccessibilityForTextChange):
+        (WebCore::EditCommand::setParent): Deleted.
+        * editing/EditCommand.h:
+        * editing/EditingAllInOne.cpp:
+        * editing/Editor.cpp:
+        (WebCore::Editor::handleTextEvent):
+        (WebCore::Editor::deleteSelectionWithSmartDelete):
+        (WebCore::Editor::replaceSelectionWithFragment):
+        (WebCore::Editor::replaceSelectionWithText):
+        (WebCore::Editor::appliedEditing):
+        (WebCore::Editor::unappliedEditing):
+        (WebCore::Editor::performCutOrCopy):
+        (WebCore::Editor::markMisspellingsAfterTypingToWord):
+        (WebCore::Editor::changeBackToReplacedString):
+        (WebCore::Editor::transpose):
+        (WebCore::Editor::changeSelectionAfterCommand):
+        * editing/Editor.h:
+        * editing/EditorCommand.cpp:
+        (WebCore::executeInsertFragment):
+        * editing/FrameSelection.cpp:
+        (WebCore::FrameSelection::moveTo):
+        (WebCore::FrameSelection::moveWithoutValidationTo):
+        (WebCore::FrameSelection::setSelectionByMouseIfDifferent):
+        (WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance):
+        (WebCore::FrameSelection::setSelection):
+        (WebCore::FrameSelection::updateAndRevealSelection):
+        (WebCore::isBoundary):
+        (WebCore::FrameSelection::textSelectionIntent):
+        (WebCore::FrameSelection::modify):
+        (WebCore::FrameSelection::selectAll):
+        (WebCore::FrameSelection::wordSelectionContainingCaretSelection):
+        (WebCore::FrameSelection::modifyMovingBackward): Deleted.
+        (WebCore::FrameSelection::selectFrameElementInParentIfFullySelected): Deleted.
+        (WebCore::FrameSelection::selectionAtWordStart): Deleted.
+        * editing/FrameSelection.h:
+        (WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
+        (WebCore::FrameSelection::selection): Deleted.
+        * editing/InsertIntoTextNodeCommand.cpp:
+        (WebCore::InsertIntoTextNodeCommand::InsertIntoTextNodeCommand):
+        (WebCore::InsertIntoTextNodeCommand::doApply):
+        (WebCore::InsertIntoTextNodeCommand::doUnapply):
+        * editing/InsertIntoTextNodeCommand.h:
+        (WebCore::InsertIntoTextNodeCommand::create):
+        (WebCore::InsertIntoTextNodeCommand::insertedText):
+        * editing/InsertNodeBeforeCommand.cpp:
+        (WebCore::InsertNodeBeforeCommand::InsertNodeBeforeCommand):
+        (WebCore::InsertNodeBeforeCommand::doApply):
+        (WebCore::InsertNodeBeforeCommand::doUnapply):
+        * editing/InsertNodeBeforeCommand.h:
+        (WebCore::InsertNodeBeforeCommand::create):
+        * editing/InsertParagraphSeparatorCommand.cpp:
+        (WebCore::InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand):
+        * editing/InsertParagraphSeparatorCommand.h:
+        (WebCore::InsertParagraphSeparatorCommand::create):
+        * editing/InsertTextCommand.cpp:
+        (WebCore::InsertTextCommand::InsertTextCommand):
+        * editing/InsertTextCommand.h:
+        (WebCore::InsertTextCommand::create):
+        (WebCore::InsertTextCommand::createWithMarkerSupplier):
+        * editing/MoveSelectionCommand.cpp:
+        (WebCore::MoveSelectionCommand::doApply):
+        * editing/RemoveNodePreservingChildrenCommand.cpp:
+        (WebCore::RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand):
+        * editing/RemoveNodePreservingChildrenCommand.h:
+        (WebCore::RemoveNodePreservingChildrenCommand::create):
+        * editing/ReplaceDeleteFromTextNodeCommand.cpp: Copied from Source/WebCore/editing/AppendNodeCommand.h.
+        (WebCore::ReplaceDeleteFromTextNodeCommand::ReplaceDeleteFromTextNodeCommand):
+        (WebCore::ReplaceDeleteFromTextNodeCommand::notifyAccessibilityForTextChange):
+        * editing/ReplaceDeleteFromTextNodeCommand.h: Copied from Source/WebCore/editing/AppendNodeCommand.h.
+        * editing/ReplaceInsertIntoTextNodeCommand.cpp: Added.
+        (WebCore::ReplaceInsertIntoTextNodeCommand::ReplaceInsertIntoTextNodeCommand):
+        (WebCore::ReplaceInsertIntoTextNodeCommand::notifyAccessibilityForTextChange):
+        * editing/ReplaceInsertIntoTextNodeCommand.h: Copied from Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h.
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::ReplaceSelectionCommand::ReplaceSelectionCommand):
+        (WebCore::ReplaceSelectionCommand::InsertedNodes::didReplaceNode): Deleted.
+        (WebCore::ReplaceSelectionCommand::insertAsListItems): Deleted.
+        * editing/ReplaceSelectionCommand.h:
+        (WebCore::ReplaceSelectionCommand::create):
+        * editing/TypingCommand.cpp:
+        (WebCore::TypingCommand::insertTextRunWithoutNewlines):
+        (WebCore::TypingCommand::insertParagraphSeparator):
+        * editing/atk/FrameSelectionAtk.cpp:
+        (WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
+        * editing/ios/DictationCommandIOS.cpp:
+        (WebCore::DictationCommandIOS::DictationCommandIOS):
+        * editing/ios/DictationCommandIOS.h:
+        * editing/mac/FrameSelectionMac.mm:
+        (WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
+        * html/HTMLTextFormControlElement.cpp:
+        (WebCore::HTMLTextFormControlElement::setInnerTextValue):
+        * page/DragController.cpp:
+        (WebCore::DragController::concludeEditDrag):
+        * page/EventHandler.cpp:
+        (WebCore::setInitialKeyboardSelection):
+        * page/FocusController.cpp:
+        (WebCore::FocusController::advanceFocusInDocumentOrder):
+
 2015-04-24  Darin Adler  <darin@apple.com>
 
         Convert OwnPtr and PassOwnPtr uses to std::unique_ptr
index 369f52b..96d8954 100644 (file)
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\editing\ReplaceDeleteFromTextNodeCommand.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="..\editing\ReplaceInsertIntoTextNodeCommand.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\editing\ReplaceNodeWithSpanCommand.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
     <ClInclude Include="..\accessibility\AccessibilityTableHeaderContainer.h" />
     <ClInclude Include="..\accessibility\AccessibilityTableRow.h" />
     <ClInclude Include="..\accessibility\AXObjectCache.h" />
+    <ClInclude Include="..\accessibility\AXTextStateChangeIntent.h" />
     <ClInclude Include="..\accessibility\win\AccessibilityObjectWrapperWin.h" />
     <ClInclude Include="..\page\AdjustViewSizeOrNot.h" />
     <ClInclude Include="..\page\AlternativeTextClient.h" />
     <ClInclude Include="..\editing\RemoveNodeCommand.h" />
     <ClInclude Include="..\editing\RemoveNodePreservingChildrenCommand.h" />
     <ClInclude Include="..\editing\RenderedPosition.h" />
+    <ClInclude Include="..\editing\ReplaceDeleteFromTextNodeCommand.h" />
+    <ClInclude Include="..\editing\ReplaceInsertIntoTextNodeCommand.h" />
     <ClInclude Include="..\editing\ReplaceNodeWithSpanCommand.h" />
     <ClInclude Include="..\editing\ReplaceSelectionCommand.h" />
     <ClInclude Include="..\editing\SetNodeAttributeCommand.h" />
index a031808..fe9fb64 100644 (file)
     <ClCompile Include="..\editing\RenderedPosition.cpp">
       <Filter>editing</Filter>
     </ClCompile>
+    <ClCompile Include="..\editing\ReplaceDeleteFromTextNodeCommand.cpp">
+      <Filter>editing</Filter>
+    </ClCompile>
+    <ClCompile Include="..\editing\ReplaceInsertIntoTextNodeCommand.cpp">
+      <Filter>editing</Filter>
+    </ClCompile>
     <ClCompile Include="..\editing\ReplaceNodeWithSpanCommand.cpp">
       <Filter>editing</Filter>
     </ClCompile>
     <ClInclude Include="..\accessibility\AXObjectCache.h">
       <Filter>accessibility</Filter>
     </ClInclude>
+    <ClInclude Include="..\accessibility\AXTextStateChangeIntent.h">
+        <Filter>accessibility</Filter>
+    </ClInclude>
     <ClInclude Include="..\accessibility\win\AccessibilityObjectWrapperWin.h">
       <Filter>accessibility\win</Filter>
     </ClInclude>
     <ClInclude Include="..\editing\RenderedPosition.h">
       <Filter>editing</Filter>
     </ClInclude>
+    <ClInclude Include="..\editing\ReplaceDeleteFromTextNodeCommand.h">
+      <Filter>editing</Filter>
+    </ClInclude>
+    <ClInclude Include="..\editing\ReplaceInsertIntoTextNodeCommand.h">
+      <Filter>editing</Filter>
+    </ClInclude>
     <ClInclude Include="..\editing\ReplaceNodeWithSpanCommand.h">
       <Filter>editing</Filter>
     </ClInclude>
index 8cc7b54..9b51361 100644 (file)
                9001774112E0347800648462 /* OESStandardDerivatives.h in Headers */ = {isa = PBXBuildFile; fileRef = 9001773E12E0347800648462 /* OESStandardDerivatives.h */; };
                9001788012E0370700648462 /* JSOESStandardDerivatives.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9001787E12E0370700648462 /* JSOESStandardDerivatives.cpp */; };
                9001788112E0370700648462 /* JSOESStandardDerivatives.h in Headers */ = {isa = PBXBuildFile; fileRef = 9001787F12E0370700648462 /* JSOESStandardDerivatives.h */; };
+               91C9F2F91AE3BEB00095B61C /* AXTextStateChangeIntent.h in Headers */ = {isa = PBXBuildFile; fileRef = 91C9F2F81AE3BE240095B61C /* AXTextStateChangeIntent.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9302B0BD0D79F82900C7EE83 /* PageGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9302B0BC0D79F82900C7EE83 /* PageGroup.cpp */; };
                9302B0BF0D79F82C00C7EE83 /* PageGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 9302B0BE0D79F82C00C7EE83 /* PageGroup.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9305B24D098F1B6B00C28855 /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9305B24C098F1B6B00C28855 /* Timer.h */; settings = {ATTRIBUTES = (Private, ); }; };
                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 */; };
                9001773F12E0347800648462 /* OESStandardDerivatives.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = OESStandardDerivatives.idl; path = canvas/OESStandardDerivatives.idl; sourceTree = "<group>"; };
                9001787E12E0370700648462 /* JSOESStandardDerivatives.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSOESStandardDerivatives.cpp; sourceTree = "<group>"; };
                9001787F12E0370700648462 /* JSOESStandardDerivatives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSOESStandardDerivatives.h; sourceTree = "<group>"; };
+               91C9F2F81AE3BE240095B61C /* AXTextStateChangeIntent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AXTextStateChangeIntent.h; sourceTree = "<group>"; };
                9302B0BC0D79F82900C7EE83 /* PageGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageGroup.cpp; sourceTree = "<group>"; };
                9302B0BE0D79F82C00C7EE83 /* PageGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageGroup.h; sourceTree = "<group>"; };
                9305B24C098F1B6B00C28855 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = "<group>"; };
                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>"; };
                                29A812130FBB9C1D00510293 /* AccessibilityTableRow.h */,
                                2981CAAF131822EC00D12F2A /* AXObjectCache.cpp */,
                                29A8121A0FBB9C1D00510293 /* AXObjectCache.h */,
+                               91C9F2F81AE3BE240095B61C /* AXTextStateChangeIntent.h */,
                        );
                        path = accessibility;
                        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 */,
                                6C568CB119DAFEA000430CA2 /* MaskImageOperation.h in Headers */,
                                0F580FAF149800D400FB5BD8 /* AnimationUtilities.h in Headers */,
                                4A4F65721AA997F100E38CDD /* RealtimeMediaSourceCapabilities.h in Headers */,
+                               F5528DA51AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.h in Headers */,
                                93309DD7099E64920056E581 /* AppendNodeCommand.h in Headers */,
                                1A8F6BBD0DB55CDC001DB794 /* ApplicationCache.h in Headers */,
                                1A8F6BBF0DB55CDC001DB794 /* ApplicationCacheGroup.h in Headers */,
                                BC60DA3A0D2A302800B9918F /* JSXMLHttpRequestException.h in Headers */,
                                F916C48E0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.h in Headers */,
                                BCDFD4960E30592F009D10AD /* JSXMLHttpRequestUpload.h in Headers */,
+                               F5528DAA1AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.h in Headers */,
                                1ACE53F70A8D19470022947D /* JSXMLSerializer.h in Headers */,
                                1A762C740A074F2600989F5B /* JSXPathEvaluator.h in Headers */,
                                BC60DB4A0D2A3D1E00B9918F /* JSXPathException.h in Headers */,
                                7C4C96DD1AD4483500365A50 /* JSReadableStream.h in Headers */,
                                83C1D428178D5AB400141E68 /* SVGPathSegCurvetoCubicRel.h in Headers */,
                                B2227A690D00BF220071B782 /* SVGPathSegCurvetoCubicSmooth.h in Headers */,
+                               91C9F2F91AE3BEB00095B61C /* AXTextStateChangeIntent.h in Headers */,
                                83C1D429178D5AB400141E68 /* SVGPathSegCurvetoCubicSmoothAbs.h in Headers */,
                                83C1D42A178D5AB400141E68 /* SVGPathSegCurvetoCubicSmoothRel.h in Headers */,
                                B2227A6D0D00BF220071B782 /* SVGPathSegCurvetoQuadratic.h in Headers */,
                                510192D118B6B9AB007FC7A1 /* ImageControlsRootElementMac.cpp in Sources */,
                                A77979190D6B9D0C003851B9 /* ImageData.cpp in Sources */,
                                97205AB51239291000B17380 /* ImageDocument.cpp in Sources */,
+                               F5528DA61AC1033E000EF7AD /* ReplaceDeleteFromTextNodeCommand.cpp in Sources */,
                                F55B3DC11251F12D003EF269 /* ImageInputType.cpp in Sources */,
                                089582550E857A7E00F82C83 /* ImageLoader.cpp in Sources */,
                                B275357B0B053814002CE64F /* ImageMac.mm in Sources */,
                                FE36FD1616C7826500F887C1 /* SQLTransactionStateMachine.cpp in Sources */,
                                1A2E6E590CC55213004A2062 /* SQLValue.cpp in Sources */,
                                4476531B133170990006B789 /* SSLKeyGeneratorIOS.cpp in Sources */,
+                               F5528DA91AC109DF000EF7AD /* ReplaceInsertIntoTextNodeCommand.cpp in Sources */,
                                93F19AE608245E59001E9ABC /* SSLKeyGeneratorMac.cpp in Sources */,
                                BC7FA62E0D1F0EFF00DB22A9 /* StaticNodeList.cpp in Sources */,
                                A5AFB34F115151A700B045CB /* StepRange.cpp in Sources */,
index a00b15c..864e160 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 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
@@ -90,6 +90,9 @@ namespace WebCore {
 
 using namespace HTMLNames;
 
+// Post value change notifications for password fields or elements contained in password fields at a 40hz interval to thwart analysis of typing cadence
+static double AccessibilityPasswordValueChangeNotificationInterval = 0.025;
+
 AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
 {
     HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
@@ -129,6 +132,7 @@ void AXObjectCache::setEnhancedUserInterfaceAccessibility(bool flag)
 AXObjectCache::AXObjectCache(Document& document)
     : m_document(document)
     , m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired)
+    , m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired)
 {
 }
 
@@ -705,7 +709,7 @@ void AXObjectCache::notificationPostTimerFired()
     Ref<Document> protectorForCacheOwner(m_document);
     m_notificationPostTimer.stop();
     
-    // In DRT, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
+    // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
     // when the notification list is cleared at the end. Instead copy this list at the start.
     auto notifications = WTF::move(m_notificationsToPost);
     
@@ -742,6 +746,20 @@ void AXObjectCache::notificationPostTimerFired()
             childrenChanged(obj->parentObject());
     }
 }
+
+void AXObjectCache::passwordNotificationPostTimerFired()
+{
+#if PLATFORM(COCOA)
+    m_passwordNotificationPostTimer.stop();
+
+    // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
+    // when the notification list is cleared at the end. Instead copy this list at the start.
+    auto notifications = WTF::move(m_passwordNotificationsToPost);
+
+    for (const auto& notification : notifications)
+        postTextStateChangePlatformNotification(notification.get(), AXTextEditTypeInsert, " ", VisiblePosition());
+#endif
+}
     
 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, PostTarget postTarget, PostType postType)
 {
@@ -850,16 +868,223 @@ void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
     postNotification(renderer, AXSelectedChildrenChanged, TargetObservableParent);
 }
 
-void AXObjectCache::nodeTextChangeNotification(Node* node, AXTextChange textChange, unsigned offset, const String& text)
+#ifndef NDEBUG
+void AXObjectCache::showIntent(const AXTextStateChangeIntent &intent)
+{
+    switch (intent.type) {
+    case AXTextStateChangeTypeUnknown:
+        dataLog("Unknown");
+        break;
+    case AXTextStateChangeTypeEdit:
+        dataLog("Edit::");
+        break;
+    case AXTextStateChangeTypeSelectionMove:
+        dataLog("Move::");
+        break;
+    case AXTextStateChangeTypeSelectionExtend:
+        dataLog("Extend::");
+        break;
+    }
+    switch (intent.type) {
+    case AXTextStateChangeTypeUnknown:
+        break;
+    case AXTextStateChangeTypeEdit:
+        switch (intent.change) {
+        case AXTextEditTypeUnknown:
+            dataLog("Unknown");
+            break;
+        case AXTextEditTypeDelete:
+            dataLog("Delete");
+            break;
+        case AXTextEditTypeInsert:
+            dataLog("Insert");
+            break;
+        case AXTextEditTypeDictation:
+            dataLog("DictationInsert");
+            break;
+        case AXTextEditTypeTyping:
+            dataLog("TypingInsert");
+            break;
+        case AXTextEditTypeCut:
+            dataLog("Cut");
+            break;
+        case AXTextEditTypePaste:
+            dataLog("Paste");
+            break;
+        }
+        break;
+    case AXTextStateChangeTypeSelectionMove:
+    case AXTextStateChangeTypeSelectionExtend:
+        switch (intent.selection.direction) {
+        case AXTextSelectionDirectionUnknown:
+            dataLog("Unknown::");
+            break;
+        case AXTextSelectionDirectionBeginning:
+            dataLog("Beginning::");
+            break;
+        case AXTextSelectionDirectionEnd:
+            dataLog("End::");
+            break;
+        case AXTextSelectionDirectionPrevious:
+            dataLog("Previous::");
+            break;
+        case AXTextSelectionDirectionNext:
+            dataLog("Next::");
+            break;
+        case AXTextSelectionDirectionDiscontiguous:
+            dataLog("Discontiguous::");
+            break;
+        }
+        switch (intent.selection.direction) {
+        case AXTextSelectionDirectionUnknown:
+        case AXTextSelectionDirectionBeginning:
+        case AXTextSelectionDirectionEnd:
+        case AXTextSelectionDirectionPrevious:
+        case AXTextSelectionDirectionNext:
+            switch (intent.selection.granularity) {
+            case AXTextSelectionGranularityUnknown:
+                dataLog("Unknown");
+                break;
+            case AXTextSelectionGranularityCharacter:
+                dataLog("Character");
+                break;
+            case AXTextSelectionGranularityWord:
+                dataLog("Word");
+                break;
+            case AXTextSelectionGranularityLine:
+                dataLog("Line");
+                break;
+            case AXTextSelectionGranularitySentence:
+                dataLog("Sentence");
+                break;
+            case AXTextSelectionGranularityParagraph:
+                dataLog("Paragraph");
+                break;
+            case AXTextSelectionGranularityPage:
+                dataLog("Page");
+                break;
+            case AXTextSelectionGranularityDocument:
+                dataLog("Document");
+                break;
+            case AXTextSelectionGranularityAll:
+                dataLog("All");
+                break;
+            }
+            break;
+        case AXTextSelectionDirectionDiscontiguous:
+            break;
+        }
+        break;
+    }
+    if (intent.isSynchronizing)
+        dataLog("-Sync");
+    dataLog("\n");
+}
+#endif
+
+void AXObjectCache::setTextSelectionIntent(AXTextStateChangeIntent intent)
+{
+    m_textSelectionIntent = intent;
+}
+
+static bool isPasswordFieldOrContainedByPasswordField(AccessibilityObject* object)
+{
+    return object && (object->isPasswordField() || object->isContainedByPasswordField());
+}
+
+void AXObjectCache::postTextStateChangeNotification(Node* node, AXTextStateChangeIntent intent, const VisibleSelection& selection)
 {
     if (!node)
         return;
 
+#if PLATFORM(COCOA)
+    if (intent.type == AXTextStateChangeTypeUnknown)
+        intent = m_textSelectionIntent;
+
     stopCachingComputedObjectAttributes();
 
-    // Delegate on the right platform
-    AccessibilityObject* obj = getOrCreate(node);
-    nodeTextChangePlatformNotification(obj, textChange, offset, text);
+    AccessibilityObject* object = getOrCreate(node);
+    if (object) {
+        if (isPasswordFieldOrContainedByPasswordField(object))
+            return;
+        object = object->observableObject();
+    }
+
+    postTextStateChangePlatformNotification(object, intent, selection);
+#else
+    postNotification(node->renderer(), AXObjectCache::AXSelectedTextChanged, TargetObservableParent);
+    UNUSED_PARAM(intent);
+    UNUSED_PARAM(selection);
+#endif
+
+    setTextSelectionIntent(AXTextStateChangeIntent());
+}
+
+void AXObjectCache::postTextStateChangeNotification(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
+{
+    if (!node)
+        return;
+    ASSERT(type != AXTextEditTypeUnknown);
+
+    stopCachingComputedObjectAttributes();
+
+    AccessibilityObject* object = getOrCreate(node);
+#if PLATFORM(COCOA)
+    if (object) {
+        if (enqueuePasswordValueChangeNotification(object))
+            return;
+        object = object->observableObject();
+    }
+
+    postTextStateChangePlatformNotification(object, type, text, position);
+#else
+    nodeTextChangePlatformNotification(object, textChangeForEditType(type), position.deepEquivalent().deprecatedEditingOffset(), text);
+#endif
+}
+
+void AXObjectCache::postTextReplacementNotification(Node* node, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
+{
+    if (!node)
+        return;
+    ASSERT(deletionType == AXTextEditTypeDelete);
+    ASSERT(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste);
+
+    stopCachingComputedObjectAttributes();
+
+    AccessibilityObject* object = getOrCreate(node);
+#if PLATFORM(COCOA)
+    if (object) {
+        if (enqueuePasswordValueChangeNotification(object))
+            return;
+        object = object->observableObject();
+    }
+
+    postTextReplacementPlatformNotification(object, deletionType, deletedText, insertionType, insertedText, position);
+#else
+    nodeTextChangePlatformNotification(object, textChangeForEditType(deletionType), position.deepEquivalent().deprecatedEditingOffset(), deletedText);
+    nodeTextChangePlatformNotification(object, textChangeForEditType(insertionType), position.deepEquivalent().deprecatedEditingOffset(), insertedText);
+#endif
+}
+
+bool AXObjectCache::enqueuePasswordValueChangeNotification(AccessibilityObject* object)
+{
+    if (!isPasswordFieldOrContainedByPasswordField(object))
+        return false;
+
+    AccessibilityObject* observableObject = object->observableObject();
+    if (!observableObject) {
+        ASSERT_NOT_REACHED();
+        // return true even though the enqueue didn't happen because this is a password field and caller shouldn't post a notification
+        return true;
+    }
+
+    if (!m_passwordNotificationsToPost.contains(observableObject)) {
+        m_passwordNotificationsToPost.append(observableObject);
+        if (!m_passwordNotificationPostTimer.isActive())
+            m_passwordNotificationPostTimer.startOneShot(AccessibilityPasswordValueChangeNotificationInterval);
+    }
+
+    return true;
 }
 
 void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent loadingEvent)
@@ -1084,6 +1309,14 @@ bool isNodeAriaVisible(Node* node)
     return false;
 }
 
+AccessibilityObject* AXObjectCache::rootWebArea()
+{
+    AccessibilityObject* rootObject = this->rootObject();
+    if (!rootObject || !rootObject->isAccessibilityScrollView())
+        return nullptr;
+    return downcast<AccessibilityScrollView>(*rootObject).webAreaObject();
+}
+
 AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache* cache)
     : m_cache(cache)
 {
@@ -1096,6 +1329,26 @@ AXAttributeCacheEnabler::~AXAttributeCacheEnabler()
     if (m_cache)
         m_cache->stopCachingComputedObjectAttributes();
 }
+
+#if !PLATFORM(COCOA)
+AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType type)
+{
+    switch (type) {
+    case AXTextEditTypeCut:
+    case AXTextEditTypeDelete:
+        return AXTextDeleted;
+    case AXTextEditTypeInsert:
+    case AXTextEditTypeDictation:
+    case AXTextEditTypeTyping:
+    case AXTextEditTypePaste:
+        return AXTextInserted;
+    case AXTextEditTypeUnknown:
+        break;
+    }
+    ASSERT_NOT_REACHED();
+    return AXTextInserted;
+}
+#endif
     
 } // namespace WebCore
 
index bfa9277..30a7a42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 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
@@ -26,6 +26,7 @@
 #ifndef AXObjectCache_h
 #define AXObjectCache_h
 
+#include "AXTextStateChangeIntent.h"
 #include "AccessibilityObject.h"
 #include "Timer.h"
 #include <limits.h>
@@ -67,6 +68,10 @@ private:
     HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
 };
 
+#if !PLATFORM(COCOA)
+enum AXTextChange { AXTextInserted, AXTextDeleted };
+#endif
+
 enum PostTarget { TargetElement, TargetObservableParent };
 
 enum PostType { PostSynchronously, PostAsynchronously };
@@ -189,12 +194,15 @@ public:
     void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
     void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
 
-    enum AXTextChange {
-        AXTextInserted,
-        AXTextDeleted,
-    };
+#ifndef NDEBUG
+    void showIntent(const AXTextStateChangeIntent&);
+#endif
+
+    void setTextSelectionIntent(AXTextStateChangeIntent);
 
-    void nodeTextChangeNotification(Node*, AXTextChange, unsigned offset, const String&);
+    void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
+    void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
+    void postTextStateChangeNotification(Node*, AXTextStateChangeIntent, const VisibleSelection&);
 
     enum AXLoadingEvent {
         AXLoadingStarted,
@@ -213,12 +221,24 @@ public:
     AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
 
     Document& document() const { return m_document; }
-    
+
+#if PLATFORM(MAC)
+    static void setShouldRepostNotificationsForTests(bool value);
+#endif
+
 protected:
     void postPlatformNotification(AccessibilityObject*, AXNotification);
     void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
 
-    void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
+#if PLATFORM(COCOA)
+    void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextStateChangeIntent, const VisibleSelection&);
+    void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
+    void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
+#else
+    static AXTextChange textChangeForEditType(AXTextEditType);
+    void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&);
+#endif
+
     void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
     void textChanged(AccessibilityObject*);
     void labelChanged(Element*);
@@ -229,6 +249,8 @@ protected:
     bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
 
 private:
+    AccessibilityObject* rootWebArea();
+    
     Document& m_document;
     HashMap<AXID, RefPtr<AccessibilityObject>> m_objects;
     HashMap<RenderObject*, AXID> m_renderObjectMapping;
@@ -244,6 +266,11 @@ private:
     Timer m_notificationPostTimer;
     Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
     void notificationPostTimerFired();
+
+    Timer m_passwordNotificationPostTimer;
+    Vector<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost;
+    void passwordNotificationPostTimerFired();
+
     void handleMenuOpened(Node*);
     void handleLiveRegionCreated(Node*);
     void handleMenuItemSelected(Node*);
@@ -251,6 +278,10 @@ private:
     static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
     
     AXID getAXID(AccessibilityObject*);
+
+    bool enqueuePasswordValueChangeNotification(AccessibilityObject*);
+
+    AXTextStateChangeIntent m_textSelectionIntent;
 };
 
 class AXAttributeCacheEnabler
@@ -308,8 +339,9 @@ inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
 inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
 inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
 inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
-inline void AXObjectCache::nodeTextChangeNotification(Node*, AXTextChange, unsigned, const String&) { }
-inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextStateChangeIntent, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
 inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }
@@ -320,6 +352,14 @@ inline void AXObjectCache::remove(Node*) { }
 inline void AXObjectCache::remove(Widget*) { }
 inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
 inline void AXObjectCache::selectedChildrenChanged(Node*) { }
+#if PLATFORM(COCOA)
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextStateChangeIntent, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
+#else
+inline AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType) { return AXTextInserted; }
+inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
+#endif
 #endif
 
 }
diff --git a/Source/WebCore/accessibility/AXTextStateChangeIntent.h b/Source/WebCore/accessibility/AXTextStateChangeIntent.h
new file mode 100644 (file)
index 0000000..95c264f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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 AXTextStateChangeIntent_h
+#define AXTextStateChangeIntent_h
+
+namespace WebCore {
+    
+enum AXTextStateChangeType {
+    AXTextStateChangeTypeUnknown,
+    AXTextStateChangeTypeEdit,
+    AXTextStateChangeTypeSelectionMove,
+    AXTextStateChangeTypeSelectionExtend
+};
+
+enum AXTextEditType {
+    AXTextEditTypeUnknown,
+    AXTextEditTypeDelete, // Generic text delete
+    AXTextEditTypeInsert, // Generic text insert
+    AXTextEditTypeTyping, // Insert via typing
+    AXTextEditTypeDictation, // Insert via dictation
+    AXTextEditTypeCut, // Delete via Cut
+    AXTextEditTypePaste // Insert via Paste
+};
+
+enum AXTextSelectionDirection {
+    AXTextSelectionDirectionUnknown,
+    AXTextSelectionDirectionBeginning,
+    AXTextSelectionDirectionEnd,
+    AXTextSelectionDirectionPrevious,
+    AXTextSelectionDirectionNext,
+    AXTextSelectionDirectionDiscontiguous
+};
+
+enum AXTextSelectionGranularity {
+    AXTextSelectionGranularityUnknown,
+    AXTextSelectionGranularityCharacter,
+    AXTextSelectionGranularityWord,
+    AXTextSelectionGranularityLine,
+    AXTextSelectionGranularitySentence,
+    AXTextSelectionGranularityParagraph,
+    AXTextSelectionGranularityPage,
+    AXTextSelectionGranularityDocument,
+    AXTextSelectionGranularityAll // All granularity represents the action of selecting the whole document as a single action. Extending selection by some other granularity until it encompasses the whole document will not result in a all granularity notification.
+};
+
+struct AXTextSelection {
+    AXTextSelectionDirection direction;
+    AXTextSelectionGranularity granularity;
+};
+
+struct AXTextStateChangeIntent {
+    AXTextStateChangeType type;
+    union {
+        AXTextSelection selection;
+        AXTextEditType change;
+    };
+    bool isSynchronizing { false };
+
+    AXTextStateChangeIntent(AXTextStateChangeType type = AXTextStateChangeTypeUnknown, AXTextSelection selection = AXTextSelection())
+        : type(type)
+        , selection(selection)
+    { }
+
+    AXTextStateChangeIntent(AXTextEditType change)
+        : type(AXTextStateChangeTypeEdit)
+        , change(change)
+    { }
+
+    AXTextStateChangeIntent(AXTextStateChangeType type, bool isSynchronizing)
+        : type(type)
+        , selection()
+        , isSynchronizing(isSynchronizing)
+    {
+        ASSERT(type == AXTextStateChangeTypeSelectionMove || type == AXTextStateChangeTypeSelectionExtend);
+    }
+};
+
+}
+
+#endif // AXTextStateChangeIntent_h
index d8d26de..438e851 100644 (file)
@@ -552,6 +552,27 @@ bool AccessibilityNodeObject::isPasswordField() const
     return inputElement->isPasswordField();
 }
 
+AccessibilityObject* AccessibilityNodeObject::passwordFieldOrContainingPasswordField()
+{
+    Node* node = this->node();
+    if (!node)
+        return nullptr;
+
+    if (HTMLInputElement* inputElement = node->toInputElement()) {
+        if (inputElement->isPasswordField())
+            return this;
+    }
+
+    Element* element = node->shadowHost();
+    if (!element || !is<HTMLInputElement>(element))
+        return nullptr;
+
+    if (AXObjectCache* cache = axObjectCache())
+        return cache->getOrCreate(element);
+
+    return nullptr;
+}
+
 bool AccessibilityNodeObject::isInputImage() const
 {
     Node* node = this->node();
index 3e7572f..a82bd81 100644 (file)
@@ -88,6 +88,7 @@ public:
     virtual bool isNativeImage() const override;
     virtual bool isNativeTextControl() const override;
     virtual bool isPasswordField() const override;
+    virtual AccessibilityObject* passwordFieldOrContainingPasswordField() override;
     virtual bool isProgressIndicator() const override;
     virtual bool isSearchField() const override;
     virtual bool isSlider() const override;
index 5a27ea8..cb33809 100644 (file)
@@ -2646,4 +2646,17 @@ void AccessibilityObject::setPreventKeyboardDOMEventDispatch(bool on)
 }
 #endif
 
+bool AccessibilityObject::isContainedByPasswordField() const
+{
+    Node* node = this->node();
+    if (!node)
+        return false;
+    
+    if (ariaRoleAttribute() != UnknownRole)
+        return false;
+
+    Element* element = node->shadowHost();
+    return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(*element).isPasswordField();
+}
+
 } // namespace WebCore
index 3586f2a..816c312 100644 (file)
@@ -463,6 +463,8 @@ public:
     virtual bool isNativeImage() const { return false; }
     virtual bool isImageButton() const { return false; }
     virtual bool isPasswordField() const { return false; }
+    bool isContainedByPasswordField() const;
+    virtual AccessibilityObject* passwordFieldOrContainingPasswordField() { return nullptr; }
     virtual bool isNativeTextControl() const { return false; }
     virtual bool isSearchField() const { return false; }
     bool isWebArea() const { return roleValue() == WebAreaRole; }
index e9d2a52..50fdadf 100644 (file)
@@ -1491,14 +1491,17 @@ PlainTextRange AccessibilityRenderObject::selectedTextRange() const
 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
 {
     if (isNativeTextControl()) {
+        if (AXObjectCache* cache = axObjectCache())
+            cache->setTextSelectionIntent(AXTextStateChangeIntent(range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove, true));
         HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
         textControl.setSelectionRange(range.start, range.start + range.length);
         return;
     }
 
     Node* node = m_renderer->node();
-    m_renderer->frame().selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
-        Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
+    VisibleSelection newSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor), Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM);
+    AXTextStateChangeIntent newIntent(range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove, true);
+    m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(), newIntent);
 }
 
 URL AccessibilityRenderObject::url() const
@@ -1963,13 +1966,16 @@ void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePos
 {
     if (range.start.isNull() || range.end.isNull())
         return;
-    
+
     // make selection and tell the document to use it. if it's zero length, then move to that position
-    if (range.start == range.end)
+    if (range.start == range.end) {
+        if (AXObjectCache* cache = axObjectCache())
+            cache->setTextSelectionIntent(AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, true));
         m_renderer->frame().selection().moveTo(range.start, UserTriggered);
+    }
     else {
         VisibleSelection newSelection = VisibleSelection(range.start, range.end);
-        m_renderer->frame().selection().setSelection(newSelection);
+        m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(), AXTextStateChangeIntent(AXTextStateChangeTypeSelectionExtend, true));
     }
 }
 
index c62c753..0c464bb 100644 (file)
@@ -43,6 +43,8 @@ public:
     virtual ~AccessibilityScrollView();
     virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
 
+    AccessibilityObject* webAreaObject() const;
+
 private:
     explicit AccessibilityScrollView(ScrollView*);
     
@@ -71,7 +73,6 @@ private:
     virtual AccessibilityObject* parentObject() const override;
     virtual AccessibilityObject* parentObjectIfExists() const override;
     
-    AccessibilityObject* webAreaObject() const;
     virtual AccessibilityObject* firstChild() const override { return webAreaObject(); }
     AccessibilityScrollbar* addChildScrollbar(Scrollbar*);
     void removeChildScrollbar(AccessibilityObject*);
index 5852a0f..ae2560b 100644 (file)
@@ -268,10 +268,10 @@ void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* obje
     // Select the right signal to be emitted
     CString detail;
     switch (textChange) {
-    case AXObjectCache::AXTextInserted:
+    case AXTextInserted:
         detail = "text-insert";
         break;
-    case AXObjectCache::AXTextDeleted:
+    case AXTextDeleted:
         detail = "text-remove";
         break;
     }
index 119e0b0..b623369 100644 (file)
@@ -95,8 +95,19 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
     [obj->wrapper() accessibilityPostedNotification:notificationString];
 }
 
-void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
+void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextStateChangeIntent, const VisibleSelection&)
 {
+    postPlatformNotification(object, AXSelectedTextChanged);
+}
+
+void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextEditType, const String&, const VisiblePosition&)
+{
+    postPlatformNotification(object, AXValueChanged);
+}
+
+void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject* object, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&)
+{
+    postPlatformNotification(object, AXValueChanged);
 }
 
 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)
index b300e77..b0a5586 100644 (file)
 #define NSAccessibilityLiveRegionCreatedNotification @"AXLiveRegionCreated"
 #endif
 
+#ifndef NSAccessibilityTextStateChangeTypeKey
+#define NSAccessibilityTextStateChangeTypeKey @"AXTextStateChangeType"
+#endif
+
+#ifndef NSAccessibilityTextStateSyncKey
+#define NSAccessibilityTextStateSyncKey @"AXTextStateSync"
+#endif
+
+#ifndef NSAccessibilityTextSelectionDirection
+#define NSAccessibilityTextSelectionDirection @"AXTextSelectionDirection"
+#endif
+
+#ifndef NSAccessibilityTextSelectionGranularity
+#define NSAccessibilityTextSelectionGranularity @"AXTextSelectionGranularity"
+#endif
+
+#ifndef NSAccessibilityTextEditType
+#define NSAccessibilityTextEditType @"AXTextEditType"
+#endif
+
+#ifndef NSAccessibilityTextChangeValues
+#define NSAccessibilityTextChangeValues @"AXTextChangeValues"
+#endif
+
+#ifndef NSAccessibilityTextChangeValue
+#define NSAccessibilityTextChangeValue @"AXTextChangeValue"
+#endif
+
+#ifndef NSAccessibilityTextChangeValueLength
+#define NSAccessibilityTextChangeValueLength @"AXTextChangeValueLength"
+#endif
+
+#ifndef NSAccessibilityTextChangeValueStartMarker
+#define NSAccessibilityTextChangeValueStartMarker @"AXTextChangeValueStartMarker"
+#endif
+
+#ifndef NSAccessibilityTextChangeElement
+#define NSAccessibilityTextChangeElement @"AXTextChangeElement"
+#endif
+
+#ifndef NSAccessibilitySelectedTextMarkerRangeAttribute
+#define NSAccessibilitySelectedTextMarkerRangeAttribute @"AXSelectedTextMarkerRange"
+#endif
+
+// Very large strings can negatively impact the performance of notifications, so this length is chosen to try to fit an average paragraph or line of text, but not allow strings to be large enough to hurt performance.
+static const NSUInteger AXValueChangeTruncationLength = 1000;
+
 // The simple Cocoa calls in this file don't throw exceptions.
 
 namespace WebCore {
@@ -60,6 +107,21 @@ void AXObjectCache::attachWrapper(AccessibilityObject* obj)
     obj->setWrapper(wrapper.get());
 }
 
+static BOOL axShouldRepostNotificationsForTests = false;
+
+void AXObjectCache::setShouldRepostNotificationsForTests(bool value)
+{
+    axShouldRepostNotificationsForTests = value;
+}
+
+static void AXPostNotificationWithUserInfo(id object, NSString *notification, id userInfo)
+{
+    NSAccessibilityPostNotificationWithUserInfo(object, notification, userInfo);
+    // To simplify monitoring for notifications in tests, repost as a simple NSNotification instead of forcing test infrastucture to setup an IPC client and do all the translation between WebCore types and platform specific IPC types and back
+    if (UNLIKELY(axShouldRepostNotificationsForTests))
+        [object accessibilityPostedNotification:notification userInfo:userInfo];
+}
+
 void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
 {
     if (!obj)
@@ -149,15 +211,131 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
     ASSERT([obj->wrapper() accessibilityIsIgnored] || true);
 #pragma clang diagnostic pop
-    
-    NSAccessibilityPostNotification(obj->wrapper(), macNotification);
-    
-    // Used by DRT to know when notifications are posted.
-    [obj->wrapper() accessibilityPostedNotification:macNotification];
+
+    AXPostNotificationWithUserInfo(obj->wrapper(), macNotification, nil);
 }
 
-void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
+void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextStateChangeIntent intent, const VisibleSelection& selection)
 {
+    if (!object)
+        object = rootWebArea();
+
+    if (!object)
+        return;
+
+    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:5];
+    if (intent.isSynchronizing)
+        userInfo[NSAccessibilityTextStateSyncKey] = @YES;
+    if (intent.type != AXTextStateChangeTypeUnknown) {
+        userInfo[NSAccessibilityTextStateChangeTypeKey] = @(intent.type);
+        switch (intent.type) {
+        case AXTextStateChangeTypeSelectionMove:
+        case AXTextStateChangeTypeSelectionExtend:
+            userInfo[NSAccessibilityTextSelectionDirection] = @(intent.selection.direction);
+            switch (intent.selection.direction) {
+            case AXTextSelectionDirectionUnknown:
+                break;
+            case AXTextSelectionDirectionBeginning:
+            case AXTextSelectionDirectionEnd:
+            case AXTextSelectionDirectionPrevious:
+            case AXTextSelectionDirectionNext:
+                userInfo[NSAccessibilityTextSelectionGranularity] = @(intent.selection.granularity);
+                break;
+            case AXTextSelectionDirectionDiscontiguous:
+                break;
+            }
+            break;
+        case AXTextStateChangeTypeUnknown:
+        case AXTextStateChangeTypeEdit:
+            break;
+        }
+    }
+    if (!selection.isNone()) {
+        if (id textMarkerRange = [object->wrapper() textMarkerRangeFromVisiblePositions:selection.visibleStart() endPosition:selection.visibleEnd()])
+            userInfo[NSAccessibilitySelectedTextMarkerRangeAttribute] = textMarkerRange;
+    }
+
+    if (id wrapper = object->wrapper())
+        userInfo[NSAccessibilityTextChangeElement] = wrapper;
+
+    AccessibilityObject* webArea = rootWebArea();
+    AXPostNotificationWithUserInfo(webArea->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
+    AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilitySelectedTextChangedNotification, userInfo);
+    [userInfo release];
+}
+
+static NSDictionary *textReplacementChangeDictionary(AccessibilityObject* object, AXTextEditType type, const String& string, const VisiblePosition& position)
+{
+    NSString *text = (NSString *)string;
+    NSUInteger length = [text length];
+    if (!length)
+        return nil;
+    NSMutableDictionary *change = [[NSMutableDictionary alloc] initWithCapacity:4];
+    change[NSAccessibilityTextEditType] = @(type);
+    if (length > AXValueChangeTruncationLength) {
+        change[NSAccessibilityTextChangeValueLength] = @(length);
+        text = [text substringToIndex:AXValueChangeTruncationLength];
+    }
+    change[NSAccessibilityTextChangeValue] = text;
+    if (position.isNotNull()) {
+        if (id textMarker = [object->wrapper() textMarkerForVisiblePosition:position])
+            change[NSAccessibilityTextChangeValueStartMarker] = textMarker;
+    }
+    return [change autorelease];
+}
+
+void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* object, AXTextEditType type, const String& text, const VisiblePosition& position)
+{
+    if (!object)
+        object = rootWebArea();
+
+    if (!object)
+        return;
+
+    if (!text.length())
+        return;
+
+    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:3];
+    userInfo[NSAccessibilityTextStateChangeTypeKey] = @(AXTextStateChangeTypeEdit);
+
+    if (NSDictionary *change = textReplacementChangeDictionary(object, type, text, position))
+        userInfo[NSAccessibilityTextChangeValues] = @[change];
+
+    if (id wrapper = object->wrapper())
+        userInfo[NSAccessibilityTextChangeElement] = wrapper;
+
+    AccessibilityObject* webArea = rootWebArea();
+    AXPostNotificationWithUserInfo(webArea->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    [userInfo release];
+}
+
+void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject* object, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
+{
+    if (!object)
+        object = rootWebArea();
+
+    if (!object)
+        return;
+
+    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:4];
+    userInfo[NSAccessibilityTextStateChangeTypeKey] = @(AXTextStateChangeTypeEdit);
+
+    NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
+    if (NSDictionary *change = textReplacementChangeDictionary(object, deletionType, deletedText, position))
+        [changes addObject:change];
+    if (NSDictionary *change = textReplacementChangeDictionary(object, insertionType, insertedText, position))
+        [changes addObject:change];
+    userInfo[NSAccessibilityTextChangeValues] = changes;
+    [changes release];
+
+    if (id wrapper = object->wrapper())
+        userInfo[NSAccessibilityTextChangeElement] = wrapper;
+
+    AccessibilityObject* webArea = rootWebArea();
+    AXPostNotificationWithUserInfo(webArea->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    [userInfo release];
 }
 
 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)
index 5e06c35..b93418a 100644 (file)
@@ -56,8 +56,9 @@ class VisiblePosition;
 - (NSString *)ariaLandmarkRoleDescription;
 
 - (id)attachmentView;
-// Used to inform an element when a notification is posted for it. Used by DRT.
+// Used to inform an element when a notification is posted for it. Used by tests.
 - (void)accessibilityPostedNotification:(NSString *)notificationName;
+- (void)accessibilityPostedNotification:(NSString *)notificationName userInfo:(NSDictionary *)userInfo;
 
 - (CGPathRef)convertPathToScreenSpace:(WebCore::Path &)path;
 - (CGPoint)convertPointToScreenSpace:(WebCore::FloatPoint &)point;
index 5ddfc3a..77e3949 100644 (file)
@@ -383,13 +383,69 @@ static BOOL accessibilityShouldRepostNotifications;
 + (void)accessibilitySetShouldRepostNotifications:(BOOL)repost
 {
     accessibilityShouldRepostNotifications = repost;
+#if PLATFORM(MAC)
+    AXObjectCache::setShouldRepostNotificationsForTests(repost);
+#endif
 }
 
 - (void)accessibilityPostedNotification:(NSString *)notificationName
 {
+    if (accessibilityShouldRepostNotifications)
+        [self accessibilityPostedNotification:notificationName userInfo:nil];
+}
+
+static NSArray *arrayRemovingNonJSONTypes(NSArray *array)
+{
+    ASSERT([array isKindOfClass:[NSArray class]]);
+    NSMutableArray *mutableArray = [array mutableCopy];
+    for (NSUInteger i = 0; i < [mutableArray count];) {
+        id value = mutableArray[i];
+        if ([value isKindOfClass:[NSDictionary class]])
+            [mutableArray replaceObjectAtIndex:i withObject:dictionaryRemovingNonJSONTypes(value)];
+        else if ([value isKindOfClass:[NSArray class]])
+            [mutableArray replaceObjectAtIndex:i withObject:arrayRemovingNonJSONTypes(value)];
+        else if (!([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])) {
+            [mutableArray removeObjectAtIndex:i];
+            continue;
+        }
+        i++;
+    }
+    return [mutableArray autorelease];
+}
+
+static NSDictionary *dictionaryRemovingNonJSONTypes(NSDictionary *dictionary)
+{
+    ASSERT([dictionary isKindOfClass:[NSDictionary class]]);
+    NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
+    for (NSString *key in dictionary) {
+        id value = dictionary[key];
+        if ([value isKindOfClass:[NSDictionary class]])
+            mutableDictionary[key] = dictionaryRemovingNonJSONTypes(value);
+        else if ([value isKindOfClass:[NSArray class]])
+            mutableDictionary[key] = arrayRemovingNonJSONTypes(value);
+        else if (!([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]))
+            [mutableDictionary removeObjectForKey:key];
+    }
+    return [mutableDictionary autorelease];
+}
+
+- (void)accessibilityPostedNotification:(NSString *)notificationName userInfo:(NSDictionary *)userInfo
+{
     if (accessibilityShouldRepostNotifications) {
-        NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil];
-        [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:userInfo];
+        ASSERT(notificationName);
+        NSDictionary *info = nil;
+        if (userInfo) {
+            NSData *userInfoData = [NSJSONSerialization dataWithJSONObject:dictionaryRemovingNonJSONTypes(userInfo) options:(NSJSONWritingOptions)0 error:nil];
+            if (userInfoData) {
+                NSString *userInfoString = [[NSString alloc] initWithData:userInfoData encoding:NSUTF8StringEncoding];
+                if (userInfoString)
+                    info = @{@"notificationName" : notificationName, @"userInfo" : userInfoString};
+                [userInfoString release];
+            }
+        }
+        if (!info)
+            info = @{@"notificationName" : notificationName};
+        [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:info];
     }
 }
 
index 38c5cd0..1f0ecb1 100644 (file)
@@ -33,6 +33,9 @@
 
 @interface WebAccessibilityObjectWrapper : WebAccessibilityObjectWrapperBase
 
+- (id)textMarkerRangeFromVisiblePositions:(const WebCore::VisiblePosition&)startPosition endPosition:(const WebCore::VisiblePosition&)endPosition;
+- (id)textMarkerForVisiblePosition:(const WebCore::VisiblePosition&)visiblePos;
+
 @end
 
 #endif // WebAccessibilityObjectWrapper_h
index facd33b..821101d 100644 (file)
@@ -1136,14 +1136,14 @@ static NSString* nsStringForReplacedNode(Node* replacedNode)
     return [attrString autorelease];
 }
 
-static id textMarkerRangeFromVisiblePositions(AXObjectCache *cache, VisiblePosition startPosition, VisiblePosition endPosition)
+static id textMarkerRangeFromVisiblePositions(AXObjectCache *cache, const VisiblePosition& startPosition, const VisiblePosition& endPosition)
 {
     id startTextMarker = textMarkerForVisiblePosition(cache, startPosition);
     id endTextMarker = textMarkerForVisiblePosition(cache, endPosition);
     return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
 }
 
-- (id)textMarkerRangeFromVisiblePositions:(VisiblePosition)startPosition endPosition:(VisiblePosition)endPosition
+- (id)textMarkerRangeFromVisiblePositions:(const VisiblePosition&)startPosition endPosition:(const VisiblePosition&)endPosition
 {
     return textMarkerRangeFromVisiblePositions(m_object->axObjectCache(), startPosition, endPosition);
 }
index 1deb4db..ce74f0d 100644 (file)
 #include "Document.h"
 #include "ExceptionCodePlaceholder.h"
 #include "RenderElement.h"
+#include "Text.h"
 #include "htmlediting.h"
 
 namespace WebCore {
 
-AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
-    : SimpleEditCommand(parent->document())
+AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node, EditAction editingAction)
+    : SimpleEditCommand(parent->document(), editingAction)
     , m_parent(parent)
     , m_node(node)
 {
@@ -46,15 +47,23 @@ AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPt
     ASSERT(m_parent->hasEditableStyle() || !m_parent->renderer());
 }
 
-static void sendAXTextChangedIgnoringLineBreaks(Node* node, AXObjectCache::AXTextChange textChange)
+static void sendAXTextChangedIgnoringLineBreaks(Node* node, AXTextEditType type)
 {
-    String nodeValue = node->nodeValue();
+    if (!node)
+        return;
+
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+
+    String text = node->nodeValue();
     // Don't consider linebreaks in this command
-    if (nodeValue == "\n")
+    if (text == "\n")
       return;
 
-    if (AXObjectCache* cache = node->document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(node, textChange, 0, nodeValue);
+    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()
@@ -64,18 +73,16 @@ void AppendNodeCommand::doApply()
 
     m_parent->appendChild(m_node.get(), IGNORE_EXCEPTION);
 
-    if (AXObjectCache::accessibilityEnabled())
-        sendAXTextChangedIgnoringLineBreaks(m_node.get(), AXObjectCache::AXTextInserted);
+    sendAXTextChangedIgnoringLineBreaks(m_node.get(), applyEditType());
 }
 
 void AppendNodeCommand::doUnapply()
 {
     if (!m_parent->hasEditableStyle())
         return;
-        
+
     // Need to notify this before actually deleting the text
-    if (AXObjectCache::accessibilityEnabled())
-        sendAXTextChangedIgnoringLineBreaks(m_node.get(), AXObjectCache::AXTextDeleted);
+    sendAXTextChangedIgnoringLineBreaks(m_node.get(), unapplyEditType());
 
     m_node->remove(IGNORE_EXCEPTION);
 }
index d64b04e..f782975 100644 (file)
@@ -32,13 +32,13 @@ namespace WebCore {
 
 class AppendNodeCommand : public SimpleEditCommand {
 public:
-    static Ref<AppendNodeCommand> create(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
+    static Ref<AppendNodeCommand> create(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node, EditAction editingAction)
     {
-        return adoptRef(*new AppendNodeCommand(parent, node));
+        return adoptRef(*new AppendNodeCommand(parent, node, editingAction));
     }
 
 private:
-    AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node>);
+    AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node>, EditAction);
 
     virtual void doApply() override;
     virtual void doUnapply() override;
index 7c418c2..cab8828 100644 (file)
@@ -123,9 +123,8 @@ RefPtr<HTMLElement> createStyleSpanElement(Document& document)
 }
 
 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, EditAction editingAction, EPropertyLevel propertyLevel)
-    : CompositeEditCommand(document)
+    : CompositeEditCommand(document, editingAction)
     , m_style(style->copy())
-    , m_editingAction(editingAction)
     , m_propertyLevel(propertyLevel)
     , m_start(endingSelection().start().downstream())
     , m_end(endingSelection().end().upstream())
@@ -136,9 +135,8 @@ ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
 }
 
 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
-    : CompositeEditCommand(document)
+    : CompositeEditCommand(document, editingAction)
     , m_style(style->copy())
-    , m_editingAction(editingAction)
     , m_propertyLevel(propertyLevel)
     , m_start(start)
     , m_end(end)
@@ -149,9 +147,8 @@ ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
 }
 
 ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
-    : CompositeEditCommand(element->document())
+    : CompositeEditCommand(element->document(), editingAction)
     , m_style(EditingStyle::create())
-    , m_editingAction(editingAction)
     , m_propertyLevel(PropertyDefault)
     , m_start(endingSelection().start().downstream())
     , m_end(endingSelection().end().upstream())
@@ -163,9 +160,8 @@ ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnl
 }
 
 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction editingAction)
-    : CompositeEditCommand(document)
+    : CompositeEditCommand(document, editingAction)
     , m_style(style->copy())
-    , m_editingAction(editingAction)
     , m_propertyLevel(PropertyDefault)
     , m_start(endingSelection().start().downstream())
     , m_end(endingSelection().end().upstream())
@@ -226,11 +222,6 @@ void ApplyStyleCommand::doApply()
     }
 }
 
-EditAction ApplyStyleCommand::editingAction() const
-{
-    return m_editingAction;
-}
-
 void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
 {
     // update document layout once before removing styles
index e99e36a..9dbe871 100644 (file)
@@ -72,7 +72,6 @@ private:
     ApplyStyleCommand(Document&, const EditingStyle*, bool (*isInlineElementToRemove)(const Element*), EditAction);
 
     virtual void doApply() override;
-    virtual EditAction editingAction() const override;
 
     // style-removal helpers
     bool isStyledInlineElementToRemove(Element*) const;
@@ -122,7 +121,6 @@ private:
     Position endPosition();
 
     RefPtr<EditingStyle> m_style;
-    EditAction m_editingAction;
     EPropertyLevel m_propertyLevel;
     Position m_start;
     Position m_end;
index 8461ee7..b541ea1 100644 (file)
@@ -58,6 +58,8 @@
 #include "RenderBlockFlow.h"
 #include "RenderText.h"
 #include "RenderedDocumentMarker.h"
+#include "ReplaceDeleteFromTextNodeCommand.h"
+#include "ReplaceInsertIntoTextNodeCommand.h"
 #include "ReplaceNodeWithSpanCommand.h"
 #include "ReplaceSelectionCommand.h"
 #include "ScopedEventQueue.h"
@@ -162,13 +164,31 @@ 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();
 }
 
-CompositeEditCommand::CompositeEditCommand(Document& document)
-    : EditCommand(document)
+CompositeEditCommand::CompositeEditCommand(Document& document, EditAction editingAction)
+    : EditCommand(document, editingAction)
 {
 }
 
@@ -187,6 +207,7 @@ void CompositeEditCommand::apply()
         case EditActionSetWritingDirection:
         case EditActionCut:
         case EditActionUnspecified:
+        case EditActionInsert:
         case EditActionDelete:
         case EditActionDictation:
             break;
@@ -296,7 +317,7 @@ void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
 
 void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
 {
-    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
+    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea, editingAction()));
 }
 
 void CompositeEditCommand::insertLineBreak()
@@ -322,7 +343,7 @@ bool CompositeEditCommand::isRemovableBlock(const Node* node)
 
 void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
 {
-    applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable));
+    applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable, editingAction()));
 }
 
 void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild)
@@ -373,7 +394,7 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi
 void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<ContainerNode> parent)
 {
     ASSERT(canHaveChildrenForEditing(parent.get()));
-    applyCommandToComposite(AppendNodeCommand::create(parent, node));
+    applyCommandToComposite(AppendNodeCommand::create(parent, node, editingAction()));
 }
 
 void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned from, unsigned to)
@@ -397,7 +418,7 @@ void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContent
 
 void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
 {
-    applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable));
+    applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable, editingAction()));
 }
 
 void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node)
@@ -525,20 +546,21 @@ void CompositeEditCommand::inputText(const String& text, bool selectInsertedText
 void CompositeEditCommand::insertTextIntoNode(PassRefPtr<Text> node, unsigned offset, const String& text)
 {
     if (!text.isEmpty())
-        applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text));
+        applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text, editingAction()));
 }
 
 void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
 {
-    applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
+    applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count, editingAction()));
 }
 
 void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
 {
     RefPtr<Text> node(prpNode);
-    applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
+    RefPtr<DeleteFromTextNodeCommand> deleteCommand = ReplaceDeleteFromTextNodeCommand::create(WTF::move(node), offset, count);
+    applyCommandToComposite(deleteCommand);
     if (!replacementText.isEmpty())
-        applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText));
+        applyCommandToComposite(ReplaceInsertIntoTextNodeCommand::create(WTF::move(node), offset, replacementText, deleteCommand->deletedText(), editingAction()));
 }
 
 Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
@@ -1289,7 +1311,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph;
     if (!preserveStyle)
         options |= ReplaceSelectionCommand::MatchStyle;
-    applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options));
+    applyCommandToComposite(ReplaceSelectionCommand::create(document(), WTF::move(fragment), options));
 
     frame().editor().markMisspellingsAndBadGrammar(endingSelection());
 
index c2b489d..7044c45 100644 (file)
@@ -59,6 +59,8 @@ public:
     virtual void getNodesInCommand(HashSet<Node*>&);
 #endif
 
+    AXTextEditType unapplyEditType() const;
+
 private:
     EditCommandComposition(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
 
@@ -89,7 +91,7 @@ public:
     virtual bool shouldStopCaretBlinking() const { return false; }
 
 protected:
-    explicit CompositeEditCommand(Document&);
+    explicit CompositeEditCommand(Document&, EditAction = EditActionUnspecified);
 
     //
     // sugary-sweet convenience functions to help create and apply edit commands in composite commands
index 2ea96d5..f93cc29 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2008, 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
@@ -34,8 +34,8 @@
 
 namespace WebCore {
 
-DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(PassRefPtr<Text> node, unsigned offset, unsigned count)
-    : SimpleEditCommand(node->document())
+DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(RefPtr<Text>&& node, unsigned offset, unsigned count, EditAction editingAction)
+    : SimpleEditCommand(node->document(), editingAction)
     , m_node(node)
     , m_offset(offset)
     , m_count(count)
@@ -58,8 +58,8 @@ void DeleteFromTextNodeCommand::doApply()
         return;
     
     // Need to notify this before actually deleting the text
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextDeleted, m_offset, m_text);
+    if (AXObjectCache::accessibilityEnabled())
+        notifyAccessibilityForTextChange(m_node.get(), applyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 
     m_node->deleteData(m_offset, m_count, ec);
 }
@@ -73,8 +73,8 @@ void DeleteFromTextNodeCommand::doUnapply()
 
     m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
 
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextInserted, m_offset, m_text);
+    if (AXObjectCache::accessibilityEnabled())
+        notifyAccessibilityForTextChange(m_node.get(), unapplyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 }
 
 #ifndef NDEBUG
index 2491f05..4e61969 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2008, 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
@@ -34,14 +34,17 @@ class Text;
 
 class DeleteFromTextNodeCommand : public SimpleEditCommand {
 public:
-    static Ref<DeleteFromTextNodeCommand> create(PassRefPtr<Text> node, unsigned offset, unsigned count)
+    static Ref<DeleteFromTextNodeCommand> create(RefPtr<Text>&& node, unsigned offset, unsigned count, EditAction editingAction = EditActionDelete)
     {
-        return adoptRef(*new DeleteFromTextNodeCommand(node, offset, count));
+        return adoptRef(*new DeleteFromTextNodeCommand(WTF::move(node), offset, count, editingAction));
     }
 
-private:
-    DeleteFromTextNodeCommand(PassRefPtr<Text>, unsigned offset, unsigned count);
+    const String& deletedText();
+
+protected:
+    DeleteFromTextNodeCommand(RefPtr<Text>&&, unsigned offset, unsigned count, EditAction);
 
+private:
     virtual void doApply() override;
     virtual void doUnapply() override;
     
@@ -55,6 +58,11 @@ private:
     String m_text;
 };
 
+inline const String& DeleteFromTextNodeCommand::deletedText()
+{
+    return m_text;
+}
+
 } // namespace WebCore
 
 #endif // DeleteFromTextNodeCommand_h
index 2bbc6ed..eb3a5f3 100644 (file)
@@ -72,8 +72,8 @@ static bool isTableRowEmpty(Node* row)
     return true;
 }
 
-DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
-    : CompositeEditCommand(document)
+DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
+    : CompositeEditCommand(document, editingAction)
     , m_hasSelectionToDelete(false)
     , m_smartDelete(smartDelete)
     , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
@@ -90,8 +90,8 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
 {
 }
 
-DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
-    : CompositeEditCommand(selection.start().anchorNode()->document())
+DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
+    : CompositeEditCommand(selection.start().anchorNode()->document(), editingAction)
     , m_hasSelectionToDelete(true)
     , m_smartDelete(smartDelete)
     , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
@@ -884,14 +884,6 @@ void DeleteSelectionCommand::doApply()
     clearTransientState();
 }
 
-EditAction DeleteSelectionCommand::editingAction() const
-{
-    // Note that DeleteSelectionCommand is also used when the user presses the Delete key,
-    // but in that case there's a TypingCommand that supplies the editingAction(), so
-    // the Undo menu correctly shows "Undo Typing"
-    return EditActionCut;
-}
-
 // Normally deletion doesn't preserve the typing style that was present before it.  For example,
 // type a character, Bold, then delete the character and start typing.  The Bold typing style shouldn't
 // stick around.  Deletion should preserve a typing style that *it* sets, however.
index e5bf51d..0a9b9cc 100644 (file)
@@ -34,23 +34,22 @@ class EditingStyle;
 
 class DeleteSelectionCommand : public CompositeEditCommand { 
 public:
-    static Ref<DeleteSelectionCommand> create(Document& document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
+    static Ref<DeleteSelectionCommand> create(Document& document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true, EditAction editingAction = EditActionDelete)
     {
-        return adoptRef(*new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+        return adoptRef(*new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, editingAction));
     }
-    static Ref<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
+    static Ref<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true, EditAction editingAction = EditActionDelete)
     {
-        return adoptRef(*new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+        return adoptRef(*new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, editingAction));
     }
 
 protected:
-    DeleteSelectionCommand(Document&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool santizeMarkup);
+    DeleteSelectionCommand(Document&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool santizeMarkup, EditAction = EditActionDelete);
 
 private:
-    DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup);
+    DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction);
 
     virtual void doApply();
-    virtual EditAction editingAction() const;
     
     virtual bool preservesTypingStyle() const;
 
index c173df4..908fd77 100644 (file)
@@ -115,7 +115,7 @@ void DictationCommand::insertTextRunWithoutNewlines(size_t lineStart, size_t lin
 {
     Vector<DictationAlternative> alternativesInLine;
     collectDictationAlternativesInRange(lineStart, lineLength, alternativesInLine);
-    RefPtr<InsertTextCommand> command = InsertTextCommand::createWithMarkerSupplier(document(), m_textToInsert.substring(lineStart, lineLength), DictationMarkerSupplier::create(alternativesInLine));
+    RefPtr<InsertTextCommand> command = InsertTextCommand::createWithMarkerSupplier(document(), m_textToInsert.substring(lineStart, lineLength), DictationMarkerSupplier::create(alternativesInLine), EditActionDictation);
     applyCommandToComposite(command, endingSelection());
 }
 
@@ -124,7 +124,7 @@ void DictationCommand::insertParagraphSeparator()
     if (!canAppendNewLineFeedToSelection(endingSelection()))
         return;
 
-    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionDictation));
 }
 
 void DictationCommand::collectDictationAlternativesInRange(size_t rangeStart, size_t rangeLength, Vector<DictationAlternative>& alternatives)
index 637d1eb..6ef3506 100644 (file)
@@ -29,6 +29,7 @@
 namespace WebCore {
     typedef enum {
         EditActionUnspecified,
+        EditActionInsert,
         EditActionSetColor,
         EditActionSetBackgroundColor,
         EditActionTurnOffKerning,
index 8f56532..6478ae6 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "EditCommand.h"
 
+#include "AXObjectCache.h"
 #include "CompositeEditCommand.h"
 #include "Document.h"
 #include "Editor.h"
 
 namespace WebCore {
 
-EditCommand::EditCommand(Document& document)
+EditCommand::EditCommand(Document& document, EditAction editingAction)
     : m_document(document)
     , m_parent(0)
+    , m_editingAction(editingAction)
 {
     ASSERT(document.frame());
     setStartingSelection(m_document->frame()->selection().selection());
@@ -67,7 +69,7 @@ Frame& EditCommand::frame()
 
 EditAction EditCommand::editingAction() const
 {
-    return EditActionUnspecified;
+    return m_editingAction;
 }
 
 static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
@@ -112,6 +114,50 @@ void EditCommand::setParent(CompositeEditCommand* parent)
     }
 }
 
+AXTextEditType EditCommand::applyEditType() const
+{
+    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;
+    // 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;
+    }
+    return AXTextEditTypeUnknown;
+}
+
+SimpleEditCommand::SimpleEditCommand(Document& document, EditAction editingAction)
+    : EditCommand(document, editingAction)
+{
+}
+
 void SimpleEditCommand::doReapply()
 {
     doApply();
@@ -125,4 +171,14 @@ 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 03e6c37..1a997c9 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef EditCommand_h
 #define EditCommand_h
 
+#include "AXTextStateChangeIntent.h"
 #include "EditAction.h"
 #include "VisibleSelection.h"
 
@@ -46,7 +47,7 @@ public:
 
     void setParent(CompositeEditCommand*);
 
-    virtual EditAction editingAction() const;
+    EditAction editingAction() const;
 
     const VisibleSelection& startingSelection() const { return m_startingSelection; }
     const VisibleSelection& endingSelection() const { return m_endingSelection; }
@@ -59,8 +60,11 @@ public:
 
     virtual void doApply() = 0;
 
+    AXTextEditType applyEditType() const;
+    AXTextEditType unapplyEditType() const;
+
 protected:
-    explicit EditCommand(Document&);
+    explicit EditCommand(Document&, EditAction = EditActionUnspecified);
     EditCommand(Document&, const VisibleSelection&, const VisibleSelection&);
 
     Frame& frame();
@@ -74,6 +78,7 @@ private:
     VisibleSelection m_startingSelection;
     VisibleSelection m_endingSelection;
     CompositeEditCommand* m_parent;
+    EditAction m_editingAction { EditActionUnspecified };
 };
 
 enum ShouldAssumeContentIsAlwaysEditable {
@@ -91,12 +96,14 @@ public:
 #endif
 
 protected:
-    explicit SimpleEditCommand(Document& document) : EditCommand(document) { }
+    explicit SimpleEditCommand(Document&, EditAction = EditActionUnspecified);
 
 #ifndef NDEBUG
     void addNodeAndDescendants(Node*, HashSet<Node*>&);
 #endif
 
+    virtual void notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&);
+
 private:
     virtual bool isSimpleEditCommand() const override { return true; }
 };
index dc3ae33..23bc6b1 100644 (file)
@@ -59,6 +59,8 @@
 #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 0fd2f91..a77286a 100644 (file)
@@ -208,12 +208,12 @@ bool Editor::handleTextEvent(TextEvent* event)
             if (client()->performsTwoStepPaste(event->pastingFragment()))
                 return true;
 #endif
-            replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle(), event->mailBlockquoteHandling());
+            replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle(), EditActionPaste, event->mailBlockquoteHandling());
 #if PLATFORM(IOS)
         }
 #endif
         else 
-            replaceSelectionWithText(event->data(), false, event->shouldSmartReplace());
+            replaceSelectionWithText(event->data(), false, event->shouldSmartReplace(), EditActionPaste);
         return true;
     }
 
@@ -374,12 +374,12 @@ bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity g
     return true;
 }
 
-void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
+void Editor::deleteSelectionWithSmartDelete(bool smartDelete, EditAction editingAction)
 {
     if (m_frame.selection().isNone())
         return;
 
-    applyCommand(DeleteSelectionCommand::create(document(), smartDelete));
+    applyCommand(DeleteSelectionCommand::create(document(), smartDelete, true, false, false, true, editingAction));
 }
 
 void Editor::clearText()
@@ -520,7 +520,7 @@ bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRef
     return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
 }
 
-void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, EditAction editingAction, MailBlockquoteHandling mailBlockquoteHandling)
 {
     VisibleSelection selection = m_frame.selection().selection();
     if (selection.isNone() || !selection.isContentEditable() || !fragment)
@@ -536,7 +536,7 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
     if (mailBlockquoteHandling == MailBlockquoteHandling::IgnoreBlockquote)
         options |= ReplaceSelectionCommand::IgnoreMailBlockquote;
 
-    applyCommand(ReplaceSelectionCommand::create(document(), fragment, options, EditActionPaste));
+    applyCommand(ReplaceSelectionCommand::create(document(), fragment, options, editingAction));
     revealSelectionAfterEditingOperation();
 
     selection = m_frame.selection().selection();
@@ -550,13 +550,13 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
     m_spellChecker->requestCheckingFor(SpellCheckRequest::create(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
 }
 
-void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
+void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace, EditAction editingAction)
 {
     RefPtr<Range> range = selectedRange();
     if (!range)
         return;
 
-    replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true);
+    replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true, editingAction);
 }
 
 PassRefPtr<Range> Editor::selectedRange()
@@ -998,7 +998,8 @@ 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);
+    
+    changeSelectionAfterCommand(newSelection, options, cmd->applyEditType());
     dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
 
     updateEditorUINowIfScheduled();
@@ -1029,7 +1030,7 @@ void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
     notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
 
     VisibleSelection newSelection(cmd->startingSelection());
-    changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
+    changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions(), cmd->unapplyEditType());
     dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
 
     updateEditorUINowIfScheduled();
@@ -1263,7 +1264,7 @@ void Editor::performCutOrCopy(EditorActionSpecifier action)
 
     didWriteSelectionToPasteboard();
     if (action == CutAction)
-        deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
+        deleteSelectionWithSmartDelete(canSmartCopyOrDelete(), EditActionCut);
 }
 
 void Editor::paste()
@@ -2235,7 +2236,7 @@ void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart,
 
         if (!m_frame.editor().shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
             return;
-        m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false);
+        m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false, EditActionInsert);
 
         // Reset the charet one character further.
         m_frame.selection().moveTo(m_frame.selection().selection().end());
@@ -2581,7 +2582,7 @@ void Editor::changeBackToReplacedString(const String& replacedString)
     
     m_alternativeTextController->recordAutocorrectionResponseReversed(replacedString, selection);
     TextCheckingParagraph paragraph(selection);
-    replaceSelectionWithText(replacedString, false, false);
+    replaceSelectionWithText(replacedString, false, false, EditActionInsert);
     RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
     changedRange->startContainer()->document().markers().addMarker(changedRange.get(), DocumentMarker::Replacement, String());
     m_alternativeTextController->markReversed(changedRange.get());
@@ -2815,7 +2816,7 @@ void Editor::transpose()
     // Insert the transposed characters.
     if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
         return;
-    replaceSelectionWithText(transposed, false, false);
+    replaceSelectionWithText(transposed, false, false, EditActionInsert);
 }
 
 void Editor::addToKillRing(Range* range, bool prepend)
@@ -2847,7 +2848,7 @@ void Editor::dismissCorrectionPanelAsIgnored()
     m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored);
 }
 
-void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection,  FrameSelection::SetSelectionOptions options)
+void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options, AXTextStateChangeIntent intent)
 {
     // If the new selection is orphaned, then don't update the selection.
     if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
@@ -2859,7 +2860,7 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection,
     // 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);
+        m_frame.selection().setSelection(newSelection, options, intent);
 
     // 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 a57d42a..1313db3 100644 (file)
@@ -174,7 +174,7 @@ public:
 #endif
 
     WEBCORE_EXPORT bool deleteWithDirection(SelectionDirection, TextGranularity, bool killRing, bool isTypingAction);
-    WEBCORE_EXPORT void deleteSelectionWithSmartDelete(bool smartDelete);
+    WEBCORE_EXPORT void deleteSelectionWithSmartDelete(bool smartDelete, EditAction = EditActionDelete);
     void clearText();
 #if PLATFORM(IOS)
     WEBCORE_EXPORT void removeUnchangeableStyles();
@@ -382,8 +382,8 @@ public:
     void textDidChangeInTextArea(Element*);
     WEBCORE_EXPORT WritingDirection baseWritingDirectionForSelectionStart() const;
 
-    WEBCORE_EXPORT void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
-    WEBCORE_EXPORT void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace);
+    WEBCORE_EXPORT void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle, EditAction = EditActionInsert, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
+    WEBCORE_EXPORT void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace, EditAction = EditActionInsert);
     WEBCORE_EXPORT bool selectionStartHasMarkerFor(DocumentMarker::MarkerType, int from, int length) const;
     void updateMarkersForWordsAffectedByEditing(bool onlyHandleWordsContainingSelection);
     void deletedAutocorrectionAtPosition(const Position&, const String& originalString);
@@ -468,7 +468,7 @@ private:
     enum SetCompositionMode { ConfirmComposition, CancelComposition };
     void setComposition(const String&, SetCompositionMode);
 
-    void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions);
+    void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions, AXTextStateChangeIntent = AXTextStateChangeIntent());
 
     enum EditorActionSpecifier { CutAction, CopyAction };
     void performCutOrCopy(EditorActionSpecifier);
index 511cb02..7a81668 100644 (file)
@@ -192,7 +192,7 @@ static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source,
 static bool executeInsertFragment(Frame& frame, PassRefPtr<DocumentFragment> fragment)
 {
     ASSERT(frame.document());
-    applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified));
+    applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionInsert));
     return true;
 }
 
index 440f366..5dcb142 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "FrameSelection.h"
 
+#include "AXObjectCache.h"
 #include "CharacterData.h"
 #include "DeleteSelectionCommand.h"
 #include "Document.h"
@@ -139,7 +140,7 @@ Element* FrameSelection::rootEditableElementOrDocumentElement() const
 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
 {
     setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()),
-        defaultSetSelectionOptions(userTriggered), align);
+        defaultSetSelectionOptions(userTriggered), AXTextStateChangeIntent(), align);
 }
 
 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
@@ -170,7 +171,8 @@ void FrameSelection::moveWithoutValidationTo(const Position& base, const Positio
     VisibleSelection newSelection;
     newSelection.setWithoutValidation(base, extent);
     newSelection.setIsDirectional(selectionHasDirection);
-    setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus));
+    AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown });
+    setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus), intent);
 }
 
 void DragCaretController::setCaretPosition(const VisiblePosition& position)
@@ -254,7 +256,13 @@ void FrameSelection::setSelectionByMouseIfDifferent(const VisibleSelection& pass
     if (m_selection == newSelection || !shouldChangeSelection(newSelection))
         return;
 
-    setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, AlignCursorOnScrollIfNeeded, granularity);
+    
+    AXTextStateChangeIntent intent;
+    if (AXObjectCache::accessibilityEnabled() && newSelection.isCaret())
+        intent = AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown });
+    else
+        intent = AXTextStateChangeIntent();
+    setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent, AlignCursorOnScrollIfNeeded, granularity);
 }
 
 bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelection& newSelectionPossiblyWithoutDirection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
@@ -276,7 +284,7 @@ bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelectio
     if (Document* newSelectionDocument = newSelection.base().document()) {
         if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
             if (newSelectionFrame != m_frame && newSelectionDocument != m_frame->document()) {
-                newSelectionFrame->selection().setSelection(newSelection, options, align, granularity);
+                newSelectionFrame->selection().setSelection(newSelection, options, AXTextStateChangeIntent(), align, granularity);
                 // It's possible that during the above set selection, this FrameSelection has been modified by
                 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
                 // the frame is about to be destroyed. If this is the case, clear our selection.
@@ -320,7 +328,7 @@ bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelectio
     return true;
 }
 
-void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
+void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, AXTextStateChangeIntent intent, CursorAlignOnScroll align, TextGranularity granularity)
 {
     if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity))
         return;
@@ -341,7 +349,7 @@ void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectio
     if (frameView && frameView->layoutPending())
         return;
 
-    updateAndRevealSelection();
+    updateAndRevealSelection(intent);
 }
 
 static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame)
@@ -360,7 +368,7 @@ void FrameSelection::setNeedsSelectionUpdate()
         view->clearSelection();
 }
 
-void FrameSelection::updateAndRevealSelection()
+void FrameSelection::updateAndRevealSelection(AXTextStateChangeIntent intent)
 {
     if (!m_pendingSelectionUpdate)
         return;
@@ -380,7 +388,7 @@ void FrameSelection::updateAndRevealSelection()
         revealSelection(alignment, RevealExtent);
     }
 
-    notifyAccessibilityForSelectionChange();
+    notifyAccessibilityForSelectionChange(intent);
 
     if (auto* client = m_frame->editor().client())
         client->didChangeSelectionAndUpdateLayout();
@@ -1021,7 +1029,75 @@ VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity
 static bool isBoundary(TextGranularity granularity)
 {
     return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
-}    
+}
+
+AXTextStateChangeIntent FrameSelection::textSelectionIntent(EAlteration alter, SelectionDirection direction, TextGranularity granularity)
+{
+    AXTextStateChangeIntent intent = AXTextStateChangeIntent();
+    bool flip = false;
+    if (alter == FrameSelection::AlterationMove) {
+        intent.type = AXTextStateChangeTypeSelectionMove;
+        flip = isRange() && directionOfSelection() == RTL;
+    } else
+        intent.type = AXTextStateChangeTypeSelectionExtend;
+    switch (granularity) {
+    case CharacterGranularity:
+        intent.selection.granularity = AXTextSelectionGranularityCharacter;
+        break;
+    case WordGranularity:
+        intent.selection.granularity = AXTextSelectionGranularityWord;
+        break;
+    case SentenceGranularity:
+    case SentenceBoundary:
+        intent.selection.granularity = AXTextSelectionGranularitySentence;
+        break;
+    case LineGranularity:
+    case LineBoundary:
+        intent.selection.granularity = AXTextSelectionGranularityLine;
+        break;
+    case ParagraphGranularity:
+    case ParagraphBoundary:
+        intent.selection.granularity = AXTextSelectionGranularityParagraph;
+        break;
+    case DocumentGranularity:
+    case DocumentBoundary:
+        intent.selection.granularity = AXTextSelectionGranularityDocument;
+        break;
+    }
+    bool boundary = false;
+    switch (granularity) {
+    case CharacterGranularity:
+    case WordGranularity:
+    case SentenceGranularity:
+    case LineGranularity:
+    case ParagraphGranularity:
+    case DocumentGranularity:
+        break;
+    case SentenceBoundary:
+    case LineBoundary:
+    case ParagraphBoundary:
+    case DocumentBoundary:
+        boundary = true;
+        break;
+    }
+    switch (direction) {
+    case DirectionRight:
+    case DirectionForward:
+        if (boundary)
+            intent.selection.direction = flip ? AXTextSelectionDirectionBeginning : AXTextSelectionDirectionEnd;
+        else
+            intent.selection.direction = flip ? AXTextSelectionDirectionPrevious : AXTextSelectionDirectionNext;
+        break;
+    case DirectionLeft:
+    case DirectionBackward:
+        if (boundary)
+            intent.selection.direction = flip ? AXTextSelectionDirectionEnd : AXTextSelectionDirectionBeginning;
+        else
+            intent.selection.direction = flip ? AXTextSelectionDirectionNext : AXTextSelectionDirectionPrevious;
+        break;
+    }
+    return intent;
+}
 
 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
 {
@@ -1077,6 +1153,11 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
         if (!wasRange && alter == AlterationMove && position == originalStartPosition)
             return false;
 
+    if (m_frame && AXObjectCache::accessibilityEnabled()) {
+        if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
+            cache->setTextSelectionIntent(textSelectionIntent(alter, direction, granularity));
+    }
+
     // Some of the above operations set an xPosForVerticalArrowNavigation.
     // Setting a selection will clear it, so save it to possibly restore later.
     // Note: the START position type is arbitrary because it is unused, it would be
@@ -1710,8 +1791,10 @@ void FrameSelection::selectAll()
 
     VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
 
-    if (shouldChangeSelection(newSelection))
-        setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent);
+    if (shouldChangeSelection(newSelection)) {
+        AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionExtend, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityAll });
+        setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent);
+    }
 }
 
 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
@@ -2423,7 +2506,7 @@ VisibleSelection FrameSelection::wordSelectionContainingCaretSelection(const Vis
 
     VisibleSelection newSelection = frameSelection.selection();
     newSelection.expandUsingGranularity(WordGranularity);
-    frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, frameSelection.granularity());
+    frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AXTextStateChangeIntent(), AlignCursorOnScrollIfNeeded, frameSelection.granularity());
 
     Position startPos(frameSelection.selection().start());
     Position endPos(frameSelection.selection().end());
index 0e4e28e..957b5d1 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef FrameSelection_h
 #define FrameSelection_h
 
+#include "AXTextStateChangeIntent.h"
 #include "EditingStyle.h"
 #include "IntRect.h"
 #include "LayoutRect.h"
@@ -144,7 +145,7 @@ public:
     void moveWithoutValidationTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus);
 
     const VisibleSelection& selection() const { return m_selection; }
-    WEBCORE_EXPORT void setSelection(const VisibleSelection&, SetSelectionOptions = defaultSetSelectionOptions(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
+    WEBCORE_EXPORT void setSelection(const VisibleSelection&, SetSelectionOptions = defaultSetSelectionOptions(), AXTextStateChangeIntent = AXTextStateChangeIntent(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
     WEBCORE_EXPORT bool setSelectedRange(Range*, EAffinity, bool closeTyping);
     WEBCORE_EXPORT void selectAll();
     WEBCORE_EXPORT void clear();
@@ -272,7 +273,7 @@ public:
 private:
     enum EPositionType { START, END, BASE, EXTENT };
 
-    void updateAndRevealSelection();
+    void updateAndRevealSelection(AXTextStateChangeIntent = AXTextStateChangeIntent());
     void updateDataDetectorsForSelection();
 
     bool setSelectionWithoutUpdatingAppearance(const VisibleSelection&, SetSelectionOptions, CursorAlignOnScroll, TextGranularity);
@@ -297,10 +298,11 @@ private:
 
     LayoutUnit lineDirectionPointForBlockDirectionNavigation(EPositionType);
 
+    AXTextStateChangeIntent textSelectionIntent(EAlteration, SelectionDirection, TextGranularity);
 #if HAVE(ACCESSIBILITY)
-    void notifyAccessibilityForSelectionChange();
+    void notifyAccessibilityForSelectionChange(AXTextStateChangeIntent = AXTextStateChangeIntent());
 #else
-    void notifyAccessibilityForSelectionChange() { }
+    void notifyAccessibilityForSelectionChange(AXTextSelectionIntent) { }
 #endif
 
     void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
@@ -367,7 +369,7 @@ inline void FrameSelection::setTypingStyle(PassRefPtr<EditingStyle> style)
 
 #if !(PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(EFL))
 #if HAVE(ACCESSIBILITY)
-inline void FrameSelection::notifyAccessibilityForSelectionChange()
+inline void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent)
 {
 }
 #endif
index 5394e7e..94af044 100644 (file)
@@ -39,8 +39,8 @@
 
 namespace WebCore {
 
-InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(PassRefPtr<Text> node, unsigned offset, const String& text)
-    : SimpleEditCommand(node->document())
+InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction)
+    : SimpleEditCommand(node->document(), editingAction)
     , m_node(node)
     , m_offset(offset)
     , m_text(text)
@@ -66,8 +66,8 @@ void InsertIntoTextNodeCommand::doApply()
 
     m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
 
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextInserted, m_offset, m_text);
+    if (AXObjectCache::accessibilityEnabled())
+        notifyAccessibilityForTextChange(m_node.get(), applyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 }
 
 #if PLATFORM(IOS)
@@ -84,8 +84,8 @@ void InsertIntoTextNodeCommand::doUnapply()
         return;
         
     // Need to notify this before actually deleting the text
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextDeleted, m_offset, m_text);
+    if (AXObjectCache::accessibilityEnabled())
+        notifyAccessibilityForTextChange(m_node.get(), unapplyEditType(), m_text, VisiblePosition(Position(m_node, m_offset)));
 
     m_node->deleteData(m_offset, m_text.length(), IGNORE_EXCEPTION);
 }
index 310bbb3..0760909 100644 (file)
@@ -34,14 +34,17 @@ class Text;
 
 class InsertIntoTextNodeCommand : public SimpleEditCommand {
 public:
-    static Ref<InsertIntoTextNodeCommand> create(PassRefPtr<Text> node, unsigned offset, const String& text)
+    static Ref<InsertIntoTextNodeCommand> create(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new InsertIntoTextNodeCommand(node, offset, text));
+        return adoptRef(*new InsertIntoTextNodeCommand(WTF::move(node), offset, text, editingAction));
     }
 
-private:
-    InsertIntoTextNodeCommand(PassRefPtr<Text> node, unsigned offset, const String& text);
+    const String& insertedText();
+
+protected:
+    InsertIntoTextNodeCommand(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction);
 
+private:
     virtual void doApply() override;
     virtual void doUnapply() override;
 #if PLATFORM(IOS)
@@ -57,6 +60,11 @@ private:
     String m_text;
 };
 
+inline const String& InsertIntoTextNodeCommand::insertedText()
+{
+    return m_text;
+}
+
 } // namespace WebCore
 
 #endif // InsertIntoTextNodeCommand_h
index d16b5d6..c6b229b 100644 (file)
 #include "AXObjectCache.h"
 #include "Document.h"
 #include "ExceptionCodePlaceholder.h"
+#include "Text.h"
 #include "htmlediting.h"
 
 namespace WebCore {
 
-InsertNodeBeforeCommand::InsertNodeBeforeCommand(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild,
-    ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
-    : SimpleEditCommand(refChild->document())
+InsertNodeBeforeCommand::InsertNodeBeforeCommand(RefPtr<Node>&& insertChild, RefPtr<Node>&& refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+    : SimpleEditCommand(refChild->document(), editingAction)
     , m_insertChild(insertChild)
     , m_refChild(refChild)
     , m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
@@ -57,8 +57,10 @@ void InsertNodeBeforeCommand::doApply()
 
     parent->insertBefore(m_insertChild.get(), m_refChild.get(), IGNORE_EXCEPTION);
 
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_insertChild.get(), AXObjectCache::AXTextInserted, 0, m_insertChild->nodeValue());
+    if (AXObjectCache::accessibilityEnabled()) {
+        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()
@@ -67,8 +69,10 @@ void InsertNodeBeforeCommand::doUnapply()
         return;
 
     // Need to notify this before actually deleting the text
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-        cache->nodeTextChangeNotification(m_insertChild.get(), AXObjectCache::AXTextDeleted, 0, m_insertChild->nodeValue());
+    if (AXObjectCache::accessibilityEnabled()) {
+        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 d4b762d..5cea21c 100644 (file)
@@ -32,22 +32,23 @@ namespace WebCore {
 
 class InsertNodeBeforeCommand : public SimpleEditCommand {
 public:
-    static Ref<InsertNodeBeforeCommand> create(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore,
-        ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+    static Ref<InsertNodeBeforeCommand> create(RefPtr<Node>&& childToInsert, RefPtr<Node>&& childToInsertBefore,
+        ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new InsertNodeBeforeCommand(childToInsert, childToInsertBefore, shouldAssumeContentIsAlwaysEditable));
+        return adoptRef(*new InsertNodeBeforeCommand(WTF::move(childToInsert), WTF::move(childToInsertBefore), shouldAssumeContentIsAlwaysEditable, editingAction));
     }
 
-private:
-    InsertNodeBeforeCommand(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore, ShouldAssumeContentIsAlwaysEditable);
+protected:
+    InsertNodeBeforeCommand(RefPtr<Node>&& childToInsert, RefPtr<Node>&& childToInsertBefore, ShouldAssumeContentIsAlwaysEditable, EditAction);
 
+private:
     virtual void doApply() override;
     virtual void doUnapply() override;
-    
+
 #ifndef NDEBUG
     virtual void getNodesInCommand(HashSet<Node*>&) override;
 #endif
-    
+
     RefPtr<Node> m_insertChild;
     RefPtr<Node> m_refChild;
     ShouldAssumeContentIsAlwaysEditable m_shouldAssumeContentIsAlwaysEditable;
index 212829c..7dd0160 100644 (file)
@@ -59,8 +59,8 @@ static Element* highestVisuallyEquivalentDivBelowRoot(Element* startBlock)
     return curBlock;
 }
 
-InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document& document, bool mustUseDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
-    : CompositeEditCommand(document)
+InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document& document, bool mustUseDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea, EditAction editingAction)
+    : CompositeEditCommand(document, editingAction)
     , m_mustUseDefaultParagraphElement(mustUseDefaultParagraphElement)
     , m_pasteBlockqutoeIntoUnquotedArea(pasteBlockqutoeIntoUnquotedArea)
 {
index 1281f0c..673419e 100644 (file)
@@ -34,13 +34,13 @@ class EditingStyle;
 
 class InsertParagraphSeparatorCommand : public CompositeEditCommand {
 public:
-    static Ref<InsertParagraphSeparatorCommand> create(Document& document, bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false)
+    static Ref<InsertParagraphSeparatorCommand> create(Document& document, bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
+        return adoptRef(*new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea, editingAction));
     }
 
 private:
-    InsertParagraphSeparatorCommand(Document&, bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea);
+    InsertParagraphSeparatorCommand(Document&, bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea, EditAction);
 
     virtual void doApply();
 
index bdd59a3..572e8ec 100644 (file)
 
 namespace WebCore {
 
-InsertTextCommand::InsertTextCommand(Document& document, const String& text, bool selectInsertedText, RebalanceType rebalanceType)
-    : CompositeEditCommand(document)
+InsertTextCommand::InsertTextCommand(Document& document, const String& text, bool selectInsertedText, RebalanceType rebalanceType, EditAction editingAction)
+    : CompositeEditCommand(document, editingAction)
     , m_text(text)
     , m_selectInsertedText(selectInsertedText)
     , m_rebalanceType(rebalanceType)
 {
 }
 
-InsertTextCommand::InsertTextCommand(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
-    : CompositeEditCommand(document)
+InsertTextCommand::InsertTextCommand(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier, EditAction editingAction)
+    : CompositeEditCommand(document, editingAction)
     , m_text(text)
     , m_selectInsertedText(false)
     , m_rebalanceType(RebalanceLeadingAndTrailingWhitespaces)
index f10d2fe..6528161 100644 (file)
@@ -49,20 +49,21 @@ public:
     };
 
     static Ref<InsertTextCommand> create(Document& document, const String& text, bool selectInsertedText = false,
-        RebalanceType rebalanceType = RebalanceLeadingAndTrailingWhitespaces)
+        RebalanceType rebalanceType = RebalanceLeadingAndTrailingWhitespaces, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new InsertTextCommand(document, text, selectInsertedText, rebalanceType));
+        return adoptRef(*new InsertTextCommand(document, text, selectInsertedText, rebalanceType, editingAction));
     }
 
-    static Ref<InsertTextCommand> createWithMarkerSupplier(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
+    static Ref<InsertTextCommand> createWithMarkerSupplier(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new InsertTextCommand(document, text, markerSupplier));
+        return adoptRef(*new InsertTextCommand(document, text, markerSupplier, editingAction));
     }
 
-private:
+protected:
+    InsertTextCommand(Document&, const String& text, PassRefPtr<TextInsertionMarkerSupplier>, EditAction);
+    InsertTextCommand(Document&, const String& text, bool selectInsertedText, RebalanceType, EditAction);
 
-    InsertTextCommand(Document&, const String& text, bool selectInsertedText, RebalanceType);
-    InsertTextCommand(Document&, const String& text, PassRefPtr<TextInsertionMarkerSupplier>);
+private:
 
     void deleteCharacter();
 
index ce88478..d6ecf6e 100644 (file)
@@ -79,7 +79,7 @@ void MoveSelectionCommand::doApply()
     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting;
     if (m_smartInsert)
         options |= ReplaceSelectionCommand::SmartReplace;
-    applyCommandToComposite(ReplaceSelectionCommand::create(document(), m_fragment, options));
+    applyCommandToComposite(ReplaceSelectionCommand::create(document(), WTF::move(m_fragment), options));
 }
 
 EditAction MoveSelectionCommand::editingAction() const
index 2c063c0..c629716 100644 (file)
@@ -31,8 +31,8 @@
 
 namespace WebCore {
 
-RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
-    : CompositeEditCommand(node->document())
+RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+    : CompositeEditCommand(node->document(), editingAction)
     , m_node(node)
     , m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
 {
index 4b91974..1b2c42d 100644 (file)
@@ -32,13 +32,13 @@ namespace WebCore {
 
 class RemoveNodePreservingChildrenCommand : public CompositeEditCommand {
 public:
-    static Ref<RemoveNodePreservingChildrenCommand> create(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+    static Ref<RemoveNodePreservingChildrenCommand> create(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
     {
-        return adoptRef(*new RemoveNodePreservingChildrenCommand(node, shouldAssumeContentIsAlwaysEditable));
+        return adoptRef(*new RemoveNodePreservingChildrenCommand(node, shouldAssumeContentIsAlwaysEditable, editingAction));
     }
 
 private:
-    explicit RemoveNodePreservingChildrenCommand(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable);
+    explicit RemoveNodePreservingChildrenCommand(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable, EditAction);
 
     virtual void doApply();
 
diff --git a/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp b/Source/WebCore/editing/ReplaceDeleteFromTextNodeCommand.cpp
new file mode 100644 (file)
index 0000000..a98a99d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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(WTF::move(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
new file mode 100644 (file)
index 0000000..2c72129
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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(WTF::move(text), offset, count));
+    }
+
+private:
+    ReplaceDeleteFromTextNodeCommand(RefPtr<Text>&&, unsigned, unsigned);
+    virtual 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
new file mode 100644 (file)
index 0000000..8348c11
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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(WTF::move(node), offset, text, editingAction)
+    , m_deletedText(deletedText)
+{
+}
+
+void ReplaceInsertIntoTextNodeCommand::notifyAccessibilityForTextChange(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
+{
+    if (!AXObjectCache::accessibilityEnabled())
+        return;
+    AXObjectCache* cache = document().existingAXObjectCache();
+    if (!cache)
+        return;
+    switch (type) {
+    case AXTextEditTypeUnknown:
+        break;
+    case AXTextEditTypeCut:
+    case AXTextEditTypeDictation:
+        ASSERT_NOT_REACHED();
+        break;
+    case AXTextEditTypeDelete:
+        cache->postTextReplacementNotification(node, AXTextEditTypeDelete, text, AXTextEditTypeInsert, m_deletedText, position);
+        break;
+    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
new file mode 100644 (file)
index 0000000..e263b2b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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(WTF::move(node), offset, text, deletedText, editingAction));
+    }
+
+private:
+    ReplaceInsertIntoTextNodeCommand(RefPtr<Text>&&, unsigned, const String&, const String&, EditAction);
+    virtual void notifyAccessibilityForTextChange(Node*, AXTextEditType, const String&, const VisiblePosition&) override;
+
+    String m_deletedText;
+};
+
+} // namespace WebCore
+
+#endif // ReplaceInsertIntoTextNodeCommand_h
index fb3880c..fd57455 100644 (file)
@@ -47,6 +47,8 @@
 #include "RenderInline.h"
 #include "RenderObject.h"
 #include "RenderText.h"
+#include "ReplaceDeleteFromTextNodeCommand.h"
+#include "ReplaceInsertIntoTextNodeCommand.h"
 #include "SimplifyMarkupCommand.h"
 #include "SmartReplace.h"
 #include "StyleProperties.h"
@@ -368,15 +370,14 @@ inline void ReplaceSelectionCommand::InsertedNodes::didReplaceNode(Node* node, N
         m_lastNodeInserted = newNode;
 }
 
-ReplaceSelectionCommand::ReplaceSelectionCommand(Document& document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction editAction)
-    : CompositeEditCommand(document)
+ReplaceSelectionCommand::ReplaceSelectionCommand(Document& document, RefPtr<DocumentFragment>&& fragment, CommandOptions options, EditAction editAction)
+    : CompositeEditCommand(document, editAction)
     , m_selectReplacement(options & SelectReplacement)
     , m_smartReplace(options & SmartReplace)
     , m_matchStyle(options & MatchStyle)
     , m_documentFragment(fragment)
     , m_preventNesting(options & PreventNesting)
     , m_movingParagraph(options & MovingParagraph)
-    , m_editAction(editAction)
     , m_sanitizeFragment(options & SanitizeFragment)
     , m_shouldMergeEnd(false)
     , m_ignoreMailBlockquote(options & IgnoreMailBlockquote)
@@ -1427,11 +1428,6 @@ void ReplaceSelectionCommand::mergeTextNodesAroundPosition(Position& position, P
     }
 }
 
-EditAction ReplaceSelectionCommand::editingAction() const
-{
-    return m_editAction;
-}
-
 // If the user is inserting a list into an existing list, instead of nesting the list,
 // we put the list items into the existing list.
 Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<HTMLElement> prpListElement, Node* insertionBlock, const Position& insertPos, InsertedNodes& insertedNodes)
index d87c3ff..9cfdfc7 100644 (file)
@@ -48,17 +48,16 @@ public:
 
     typedef unsigned CommandOptions;
 
-    static Ref<ReplaceSelectionCommand> create(Document& document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction action = EditActionPaste)
+    static Ref<ReplaceSelectionCommand> create(Document& document, RefPtr<DocumentFragment>&& fragment, CommandOptions options, EditAction editingAction = EditActionInsert)
     {
-        return adoptRef(*new ReplaceSelectionCommand(document, fragment, options, action));
+        return adoptRef(*new ReplaceSelectionCommand(document, WTF::move(fragment), options, editingAction));
     }
 
 private:
-    ReplaceSelectionCommand(Document&, PassRefPtr<DocumentFragment>, CommandOptions, EditAction);
+    ReplaceSelectionCommand(Document&, RefPtr<DocumentFragment>&&, CommandOptions, EditAction);
 
     virtual void doApply();
-    virtual EditAction editingAction() const;
-    
+
     class InsertedNodes {
     public:
         void respondToNodeInsertion(Node*);
@@ -120,7 +119,6 @@ private:
     RefPtr<DocumentFragment> m_documentFragment;
     bool m_preventNesting;
     bool m_movingParagraph;
-    EditAction m_editAction;
     bool m_sanitizeFragment;
     bool m_shouldMergeEnd;
     bool m_ignoreMailBlockquote;
index 9329cdf..f074448 100644 (file)
@@ -374,7 +374,7 @@ void TypingCommand::insertText(const String &text, bool selectInsertedText)
 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
 {
     RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
-        m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
+        m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces, EditActionTyping);
 
     applyCommandToComposite(command, endingSelection());
 
@@ -395,7 +395,7 @@ void TypingCommand::insertParagraphSeparator()
     if (!canAppendNewLineFeedToSelection(endingSelection()))
         return;
 
-    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionTyping));
     typingAddedToOpenCommand(InsertParagraphSeparator);
 }
 
index 7c21365..332e0ab 100644 (file)
@@ -78,7 +78,7 @@ static void maybeEmitTextFocusChange(PassRefPtr<AccessibilityObject> prpObject)
 }
 
 
-void FrameSelection::notifyAccessibilityForSelectionChange()
+void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent)
 {
     if (!AXObjectCache::accessibilityEnabled())
         return;
index c188338..134b239 100644 (file)
@@ -38,7 +38,7 @@
 namespace WebCore {
 
 DictationCommandIOS::DictationCommandIOS(Document& document, Vector<Vector<String>>&& dictationPhrases, RetainPtr<id> metadata)
-    : CompositeEditCommand(document)
+    : CompositeEditCommand(document, EditActionDictation)
     , m_dictationPhrases(WTF::move(dictationPhrases))
     , m_metadata(WTF::move(metadata))
 {
index bf90764..ac5cb21 100644 (file)
@@ -44,7 +44,6 @@ private:
     DictationCommandIOS(Document&, Vector<Vector<String>>&& dictationPhrases, RetainPtr<id> metadata);
 
     virtual void doApply() override;
-    virtual EditAction editingAction() const override { return EditActionDictation; }
 
     Vector<Vector<String>> m_dictationPhrases;
     RetainPtr<id> m_metadata;
index 9bdee1c..fd41998 100644 (file)
@@ -47,13 +47,13 @@ static CGRect accessibilityConvertScreenRect(CGRect bounds)
 #endif // !PLATFORM(IOS)
     
     
-void FrameSelection::notifyAccessibilityForSelectionChange()
+void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent intent)
 {
     Document* document = m_frame->document();
 
     if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) {
         if (AXObjectCache* cache = document->existingAXObjectCache())
-            cache->postNotification(m_selection.start().deprecatedNode()->renderer(), AXObjectCache::AXSelectedTextChanged, TargetObservableParent);
+            cache->postTextStateChangeNotification(m_selection.start().deprecatedNode(), intent, m_selection);
     }
 
 #if !PLATFORM(IOS)
index 1e6c6b2..3684e28 100644 (file)
@@ -548,16 +548,26 @@ void HTMLTextFormControlElement::setInnerTextValue(const String& value)
         return;
 
     ASSERT(isTextFormControl());
-    bool textIsChanged = value != innerTextValueFrom(*innerText);
+    String previousValue = innerTextValueFrom(*innerText);
+    bool textIsChanged = value != previousValue;
     if (textIsChanged || !innerText->hasChildNodes()) {
+#if HAVE(ACCESSIBILITY) && !PLATFORM(COCOA)
         if (textIsChanged && renderer()) {
             if (AXObjectCache* cache = document().existingAXObjectCache())
                 cache->postNotification(this, AXObjectCache::AXValueChanged, TargetObservableParent);
         }
+#endif
         innerText->setInnerText(value, ASSERT_NO_EXCEPTION);
 
         if (value.endsWith('\n') || value.endsWith('\r'))
             innerText->appendChild(HTMLBRElement::create(document()), ASSERT_NO_EXCEPTION);
+
+#if HAVE(ACCESSIBILITY) && PLATFORM(COCOA)
+        if (textIsChanged && renderer()) {
+            if (AXObjectCache* cache = document().existingAXObjectCache())
+                cache->postTextReplacementNotification(this, AXTextEditTypeDelete, previousValue, AXTextEditTypeInsert, value, VisiblePosition(Position(this, Position::PositionIsBeforeAnchor)));
+        }
+#endif
     }
 
     setFormControlValueMatchesRenderer(true);
index 99e6d59..283d23a 100644 (file)
@@ -524,7 +524,7 @@ bool DragController::concludeEditDrag(DragData& dragData)
                     options |= ReplaceSelectionCommand::SmartReplace;
                 if (chosePlainText)
                     options |= ReplaceSelectionCommand::MatchStyle;
-                applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, fragment, options));
+                applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, WTF::move(fragment), options));
             }
         }
     } else {
index 886c765..3d9fef0 100644 (file)
@@ -3239,7 +3239,8 @@ static void setInitialKeyboardSelection(Frame& frame, SelectionDirection directi
         break;
     }
 
-    selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered));
+    AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown });
+    selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
 }
 
 static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent* event)
index 77c07fe..d8d25cf 100644 (file)
@@ -340,8 +340,10 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb
     if (caretBrowsing) {
         Position position = firstPositionInOrBeforeNode(element.get());
         VisibleSelection newSelection(position, position, DOWNSTREAM);
-        if (frame.selection().shouldChangeSelection(newSelection))
-            frame.selection().setSelection(newSelection);
+        if (frame.selection().shouldChangeSelection(newSelection)) {
+            AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown });
+            frame.selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
+        }
     }
 
     element->focus(false, direction);
index 6ea5c90..dbd99cd 100644 (file)
@@ -1,3 +1,17 @@
+2015-04-24  Doug Russell  <d_russell@apple.com>
+
+        AX: richer text change notifications (142719)
+        https://bugs.webkit.org/show_bug.cgi?id=142719
+
+        Reviewed by Darin Adler.
+
+        Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.
+
+        Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.
+
+        * WebCoreSupport/WebEditorClient.mm:
+        (undoNameForEditAction):
+
 2015-04-23  Jer Noble  <jer.noble@apple.com>
 
         [Mac] Disable QTKit by default on future OS X.
index d21cdf0..e590d96 100644 (file)
@@ -546,6 +546,7 @@ static NSString* undoNameForEditAction(EditAction editAction)
     // FIXME: This is identical to code in WebKit2's WebEditCommandProxy class; would be nice to share the strings instead of having two copies.
     switch (editAction) {
         case EditActionUnspecified: return nil;
+        case EditActionInsert: return nil;
         case EditActionSetColor: return UI_STRING_KEY_INTERNAL("Set Color", "Set Color (Undo action name)", "Undo action name");
         case EditActionSetBackgroundColor: return UI_STRING_KEY_INTERNAL("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
         case EditActionTurnOffKerning: return UI_STRING_KEY_INTERNAL("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
index 4fb6c42..faec2d4 100644 (file)
@@ -1,3 +1,17 @@
+2015-04-24  Doug Russell  <d_russell@apple.com>
+
+        AX: richer text change notifications (142719)
+        https://bugs.webkit.org/show_bug.cgi?id=142719
+
+        Reviewed by Darin Adler.
+
+        Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.
+
+        Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.
+
+        * UIProcess/WebEditCommandProxy.cpp:
+        (WebKit::WebEditCommandProxy::nameForEditAction):
+
 2015-04-24  Darin Adler  <darin@apple.com>
 
         Convert OwnPtr and PassOwnPtr uses to std::unique_ptr
index 54ce03c..8785595 100644 (file)
@@ -74,6 +74,8 @@ String WebEditCommandProxy::nameForEditAction(EditAction editAction)
     switch (editAction) {
     case EditActionUnspecified:
         return String();
+    case EditActionInsert:
+        return String();
     case EditActionSetColor:
         return WEB_UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name");
     case EditActionSetBackgroundColor:
index 537f00a..26d5ac9 100644 (file)
@@ -1,3 +1,25 @@
+2015-04-24  Doug Russell  <d_russell@apple.com>
+
+        AX: richer text change notifications (142719)
+        https://bugs.webkit.org/show_bug.cgi?id=142719
+
+        Reviewed by Darin Adler.
+
+        Richer accessibility value change notifications. Introduce AXTextEditType, postTextStateChangeNotification and postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content. Also implement a mechanism to post value changes in password form fields in coalesced ticks to thwart analyzing the cadence of changes.
+
+        Richer accessibility selection change notifications. Introduce AXTextStateChangeIntent, and an overload of postTextReplacementNotification to give assistive tech apps more reliable context for responding to changes in web content selection. Also block posting selection changes on password fields.
+
+        * DumpRenderTree/mac/AccessibilityNotificationHandler.h:
+        * DumpRenderTree/mac/AccessibilityNotificationHandler.mm:
+        (-[AccessibilityNotificationHandler stopObserving]):
+        (-[AccessibilityNotificationHandler _notificationReceived:]):
+        * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+        (AccessibilityUIElement::removeNotificationListener):
+        * WebKitTestRunner/InjectedBundle/mac/AccessibilityNotificationHandler.mm:
+        (-[AccessibilityNotificationHandler _notificationReceived:]):
+        * WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
+        (WTR::AccessibilityUIElement::removeNotificationListener):
+
 2015-04-24  Darin Adler  <darin@apple.com>
 
         Convert OwnPtr and PassOwnPtr uses to std::unique_ptr
index 19386ce..908dbdc 100644 (file)
@@ -42,6 +42,7 @@
 - (void)setPlatformElement:(id)platformElement;
 - (void)setCallback:(JSObjectRef)callback;
 - (void)startObserving;
+- (void)stopObserving;
 
 @end
 
index ada687a..f1c9afd 100644 (file)
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_notificationReceived:) name:@"AXDRTNotification" object:nil];
 }
 
+- (void)stopObserving
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
 - (void)_notificationReceived:(NSNotification *)notification
 {
     NSString *notificationName = [[notification userInfo] objectForKey:@"notificationName"];
     if (m_platformElement && m_platformElement != [notification object])
         return;
 
+    NSString *userInfoJSONValue = notification.userInfo[@"userInfo"];
+
     JSRetainPtr<JSStringRef> jsNotification(Adopt, [notificationName createJSStringRef]);
     JSValueRef notificationNameArgument = JSValueMakeString([mainFrame globalContext], jsNotification.get());
+    JSValueRef userInfoJSONValueArgument = nil;
+    if ([userInfoJSONValue length]) {
+        JSRetainPtr<JSStringRef> jsUserInfoJSONValue(Adopt, [userInfoJSONValue createJSStringRef]);
+        userInfoJSONValueArgument = JSValueMakeFromJSONString([mainFrame globalContext], jsUserInfoJSONValue.get());
+    }
     if (m_platformElement) {
-        // Listener for one element just gets one argument, the notification name.
-        JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 1, &notificationNameArgument, 0);
-    } else {
-        // A global listener gets the element and the notification name as arguments.
+        // Listener for one element gets the notification name and userInfo.
         JSValueRef arguments[2];
+        arguments[0] = notificationNameArgument;
+        arguments[1] = userInfoJSONValueArgument;
+        JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 2, arguments, 0);
+    } else {
+        // A global listener gets the element, notification name and userInfo.
+        JSValueRef arguments[3];
         arguments[0] = AccessibilityUIElement::makeJSAccessibilityUIElement([mainFrame globalContext], AccessibilityUIElement([notification object]));
         arguments[1] = notificationNameArgument;
+        arguments[2] = userInfoJSONValueArgument;
         JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 2, arguments, 0);
     }
 }
index 4673a1d..397c638 100644 (file)
@@ -1446,6 +1446,7 @@ void AccessibilityUIElement::removeNotificationListener()
     // Mac programmers should not be trying to remove a listener that's already removed.
     ASSERT(m_notificationHandler);
 
+    [m_notificationHandler stopObserving];
     [m_notificationHandler release];
     m_notificationHandler = nil;
 }
index 651b591..28ca03f 100644 (file)
     if (m_platformElement && m_platformElement != [notification object])
         return;
 
+    NSString *userInfoJSONValue = notification.userInfo[@"userInfo"];
+
     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(WTR::InjectedBundle::singleton().page()->page());
     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
 
     JSRetainPtr<JSStringRef> jsNotification(Adopt, [notificationName createJSStringRef]);
     JSValueRef notificationNameArgument = JSValueMakeString(context, jsNotification.get());
+    JSValueRef userInfoJSONValueArgument = nil;
+    if ([userInfoJSONValue length]) {
+        JSRetainPtr<JSStringRef> jsUserInfoJSONValue(Adopt, [userInfoJSONValue createJSStringRef]);
+        userInfoJSONValueArgument = JSValueMakeFromJSONString(context, jsUserInfoJSONValue.get());
+    }
     if (m_platformElement) {
-        // Listener for one element just gets one argument, the notification name.
-        JSObjectCallAsFunction(context, const_cast<JSObjectRef>(m_notificationFunctionCallback), 0, 1, &notificationNameArgument, 0);
-    } else {
-        // A global listener gets the element and the notification name as arguments.
+        // Listener for one element gets the notification name and userInfo.
         JSValueRef arguments[2];
+        arguments[0] = notificationNameArgument;
+        arguments[1] = userInfoJSONValueArgument;
+        JSObjectCallAsFunction(context, const_cast<JSObjectRef>(m_notificationFunctionCallback), 0, 2, arguments, 0);
+    } else {
+        // A global listener gets the element, notification name and userInfo.
+        JSValueRef arguments[3];
         arguments[0] = toJS(context, WTF::getPtr(WTR::AccessibilityUIElement::create([notification object])));
         arguments[1] = notificationNameArgument;
+        arguments[2] = userInfoJSONValueArgument;
         JSObjectCallAsFunction(context, const_cast<JSObjectRef>(m_notificationFunctionCallback), 0, 2, arguments, 0);
     }
 }
index ed02fb7..e2607e1 100644 (file)
@@ -1476,6 +1476,7 @@ bool AccessibilityUIElement::removeNotificationListener()
     // Mac programmers should not be trying to remove a listener that's already removed.
     ASSERT(m_notificationHandler);
 
+    [m_notificationHandler stopObserving];
     [m_notificationHandler release];
     m_notificationHandler = nil;