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 ee3d29c825eb97e82000162626018f8283815a27..0a97ae4a9bbc4e9b04f8d0e9f532e68fc9d28319 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 c8e205aa4c9007a017588155a5a47e51150673b7..e7c979164be41fe4a84e3022c4edb96bdfc636a8 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 b6e2e3b81cc3741ff9c6dc0cbfbc5904b5fd0adf..87931a4a6ea0a9abbfc33cc0b22b3620284a9246 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 369f52b8d1aea37e2a649bfc861b5d01f62be8d2..96d89540b3f48e94c06255a0b0bc7001a822ffa2 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 a0318085c5a8550603f7c0e822bc46180f9e85a7..fe9fb642ad610a65c13722d1a283ab217664073b 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 8cc7b54bf00ba288b81e384ce14c83383f15c054..9b51361eeaaa76df8f14983263197bb9fcafb7e1 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 a00b15c01a607b6dda907f3615ea25572554c52a..864e160d97561bd8263bd5530d899bb50c02b0d5 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 bfa927726546390c5e4d9fd74c20c92258e398c6..30a7a42f4696aeb0d4b08d1094962d3d2814ca81 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 d8d26deb0c0178abaf30e1026442bcd5d2fa5483..438e851691d73dcc06dd7527428011e2402f0fe3 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 3e7572f3e2f7b3503bbe4c876bac8be0051304ea..a82bd81fa1012fdf16e8804f1c0f8d4d9831f521 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 5a27ea80f00780e0446b9db915a180da5952413f..cb338091802242c5257c3074428aeb44af7b1015 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 3586f2aa86319c5134d061f46d4273b92463e10e..816c3129a0b5c30452bd5893fceb630752e74a7b 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 e9d2a52a7d478caa9bffdd4b4c6246edbe03805e..50fdadfe636ab7be78c72ba936a8e30665c3e535 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 c62c753928143e6cd84f7b1789b765716820a26e..0c464bb094c1b8e06f328d6090873045c883297e 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 5852a0f27d2ff3e447922d6c682d7c469c9daf1a..ae2560b81be49c298373f1687006bb3caff4f44a 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 119e0b003cc5557677c01f834980ec174381212d..b6233696e59fce1d6bcb53342b8a2570b73eb3f6 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 b300e77fe09ad66920ff7d5e332068ecfada25f9..b0a558656efe7a6a2b232c0ff6c7d0a1aaebd5ca 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 5e06c35857976f29a00d9d6eb0b547f9b8031891..b93418a16eef4d0e4bce2729154eb50f1592220c 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 5ddfc3ab347081940531c3fe5e3d381b0f53ae30..77e39496ae439c9a7b2ebfadf6b4df6d9b7078b9 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 38c5cd0c99ceefa1d628ebd6109ada7ce5ba6f7f..1f0ecb1f5497e75ecfe5c5767017e5fb8586d2d7 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 facd33b14b9734c92a9e4b0dc2bbbcea53aed731..821101de58bce2a9c7a4db4b3cca486ecede7822 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 1deb4db025199de0d8817ba0b1375ec6d49bcab0..ce74f0df0d4af655a8d835002b658c488bbdb242 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 d64b04e8e06a01ac772e4cc0ae23c1f3c8d21d2b..f782975b892150ae36d29a6bbeca9f1dc1307c31 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 7c418c271c059c43a81aa407fb70c2262077d69b..cab882889c5e9944870e221ceb843d91cb2845f3 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 e99e36a87fdf8c00b4c9a122fd5a99766f52f62d..9dbe871da5956883bbd89e9cb63080999183b3f2 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 8461ee7ec48d959929a2f5d7f7b3870d036f06cf..b541ea1238246632b265c581dd4b6dfc005f8903 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 c2b489ddd797d32126517c49c10325d6f52faccb..7044c4553eb3c0c6308e0c32750df16d15c335ff 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 2ea96d55cafd3dac334880c55d2c91001e1612af..f93cc29e7ce71353b42034ab2039819ab20c04ae 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 2491f052392d2f33c6fc818a7ba5a3030bee1d51..4e61969f0c3d5b5122b80ec3e54d5ae3e5516c78 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 2bbc6edf2ec7e99a19e4cc87339c566491a480a1..eb3a5f30a03f284e2260ff48fba9f1b999ad9927 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 e5bf51dae37e0672be23d74af0d0a89e20b8ade0..0a9b9ccce593bf9234ba55da23efb761ecb0eef5 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 c173df45da718a2353481d85ed824e0008e0f948..908fd77b648dcb3fffd9e752ad586f1b9c495bf2 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 637d1eb53b0438763f09f0e04b704107aa9cde7e..6ef35064128e08d5e778b6c0b6f9a945e8d45acb 100644 (file)
@@ -29,6 +29,7 @@
 namespace WebCore {
     typedef enum {
         EditActionUnspecified,
+        EditActionInsert,
         EditActionSetColor,
         EditActionSetBackgroundColor,
         EditActionTurnOffKerning,
index 8f56532cce9d460483f61f844b09113cd53b6f33..6478ae6db0765ccdd5a2518ca8c79422178da95f 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 03e6c37bde5ad5892511d78a40a5e42bb974c080..1a997c921fbc08770ee18d7ed6f700852fd0d834 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 dc3ae3344766fa972122155193083ad031f8fc19..23bc6b1cde4bf6230959e072ece5e0d57f9e2b94 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 0fd2f912667f24372d3e5ff14c2dec247128cf58..a77286afb6028027c86e538b84d5fb0e0c47b241 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 a57d42a04ab0c00768a3f2f463b7c790d12a6e5a..1313db3dd01d951bde98ffadac8005ffd94fd078 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 511cb02ae8bf1cdfb1e42d5db787930b28d2fc9d..7a81668355b7ee32b056a60e3dc78f6d4b236d99 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 440f3667e80ca5d994cd38ae2b5b379eb366d178..5dcb142850fc4caf43ed799eb458942d486f54fd 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 0e4e28ee00d564ee56ab2d39c7189890ae107866..957b5d103aaecde8e4e00dcf624a2813d4580207 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 5394e7e09b182e20c3026808878dcf6664691bf1..94af0442bc163448c93fa2b475d9b76b5a0325cd 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 310bbb34065cf2a8701d2cc11c5a8f9e1671064c..076090960c8a05865be1db1099e91a1ba8693f89 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 d16b5d61e1db4c2773972611f2a26515c28754a2..c6b229b806c0dec13542de9c3da2b6caf4284d54 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 d4b762dc8dd510466e7a9b499dbdad2b7cc3e287..5cea21cd9fb1f742a928d8c7c16a55622275a066 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 212829c44939644d350fab0906a953382208a78a..7dd0160cd0343daadf1243412e3eaaff1617d65a 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 1281f0c1e9f7737ddae5bb979aff3bf9469ffa25..673419e190f515eb563382d13fcc79df9a8908f2 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 bdd59a374b58cbb0c4f3c855b2988ad68a7594dc..572e8ecbcbed79c97872af18054baa3538054a9c 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 f10d2fe7adef769d7d4cea5d9b9649fbaa956706..6528161164d138c46540679f6489ce40804b8255 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 ce884780cb18a2c53d2ed49d03405a398cf275e1..d6ecf6ee63d8684b1a193158ea07523eb665be6e 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 2c063c0155d063508964fcd3614e1a35c7eb7496..c629716feebce17cce2d3acdb03c961bd5cce0db 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 4b91974a55dc73c2491b88bf613e436bf562df5b..1b2c42d21aadf5230cfd76e3afeb7ffc1a4ed589 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 fb3880c510c44b7572b3614a05fa410b6133fc62..fd57455212060acdf2ecf0c3f632f63fc7e24631 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 d87c3ff535d50efef30f148518ef5433796bda36..9cfdfc73f77d23b1f77016300d2d29f9c41e5703 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 9329cdf39139bbbe293c79568da4b436694ea805..f074448a69e2ec02f03b1d94ea262270fb830d87 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 7c213656162e8e353b0b099c58972874372aa5ad..332e0ab811654097a6286830980eb62a1982c5b4 100644 (file)
@@ -78,7 +78,7 @@ static void maybeEmitTextFocusChange(PassRefPtr<AccessibilityObject> prpObject)
 }
 
 
-void FrameSelection::notifyAccessibilityForSelectionChange()
+void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent)
 {
     if (!AXObjectCache::accessibilityEnabled())
         return;
index c188338278f3290a9cbec6b96ebd1163b66b18b8..134b239d499cd02e178f2d811baeac451b15a292 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 bf90764b3dbd772d85f42889bf631ff317458fde..ac5cb2150e84717d3c084419ec18fe3651454dfe 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 9bdee1ccfca8b65ea74e6c71f12fff0f102d397b..fd419986047fd435e7231250c5f267aeb709b5fa 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 1e6c6b2c9b72a0593d499fcb328d600b779d6b40..3684e28ce733c8adfee88ebd228e5cc40624523c 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 99e6d598d06319883b1af2a5f7e996bff278273b..283d23a67d20364378a312560805b14cc869107c 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 886c76531dc8531d9f113bad047e7291e7e638c6..3d9fef0deb3de1df8ca855cfb40e5cfb62fcbc56 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 77c07fea6be6519b2d9a534ce0add26a72e30759..d8d25cfe8ec5c9f3e6b38a9a7591ab99f5d0ae33 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 6ea5c90c15011fe86d7b9b295e44cc4a9d9d9bad..dbd99cde8d6b8f8fa5a84b06e11d27227e87b176 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 d21cdf0bcf1ecba5e353cec442717ba218e01005..e590d96698f487099ac9b9653b46d6650e3e313d 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 4fb6c4287e66663e1cf9c832d43089d6e669d58a..faec2d41a89b007df55c0016eb6e52e844936bb7 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 54ce03c62429060795605e918914631d69756189..8785595462ff8fbe8a8bcf3f6baac601ff30b9bc 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 537f00af6033977297920cc29b122c374366f752..26d5ac91ce63850d9f6112d71c42e1c46a930703 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 19386ceb0d10e1de0e00cd595b5f1d0854b804bd..908dbdc9f7b19577b3d4f082f4d3ad2506cc6edc 100644 (file)
@@ -42,6 +42,7 @@
 - (void)setPlatformElement:(id)platformElement;
 - (void)setCallback:(JSObjectRef)callback;
 - (void)startObserving;
+- (void)stopObserving;
 
 @end
 
index ada687a41f14b0ed5babb488a329279a4d16a1d2..f1c9afd8933afab40fc0bbbac4aea6095df8f9f1 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 4673a1d2fcad4e34ed095d48906a3cafd4011ddd..397c638ae45570c9c2e90d3b42070d1e7b81f88c 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 651b59112e897fb66a83b2e75f6f0ef66f02c5b2..28ca03f3212e3e657d95a9d053435c4e0780fdd1 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 ed02fb77edccf7a4d47bf198a1167b3d2ef09645..e2607e13bb8473246bd8782e858395f7c65a5203 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;