+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
--- /dev/null
+
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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
+
--- /dev/null
+<!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>
editing/RemoveNodeCommand.cpp
editing/RemoveNodePreservingChildrenCommand.cpp
editing/RenderedPosition.cpp
+ editing/ReplaceDeleteFromTextNodeCommand.cpp
+ editing/ReplaceInsertIntoTextNodeCommand.cpp
editing/ReplaceNodeWithSpanCommand.cpp
editing/ReplaceSelectionCommand.cpp
editing/SetNodeAttributeCommand.cpp
+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
<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" />
<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>
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 */,
/*
- * 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
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);
AXObjectCache::AXObjectCache(Document& document)
: m_document(document)
, m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired)
+ , m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired)
{
}
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);
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)
{
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)
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)
{
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
/*
- * 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
#ifndef AXObjectCache_h
#define AXObjectCache_h
+#include "AXTextStateChangeIntent.h"
#include "AccessibilityObject.h"
#include "Timer.h"
#include <limits.h>
HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
};
+#if !PLATFORM(COCOA)
+enum AXTextChange { AXTextInserted, AXTextDeleted };
+#endif
+
enum PostTarget { TargetElement, TargetObservableParent };
enum PostType { PostSynchronously, PostAsynchronously };
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,
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*);
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;
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*);
static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
AXID getAXID(AccessibilityObject*);
+
+ bool enqueuePasswordValueChangeNotification(AccessibilityObject*);
+
+ AXTextStateChangeIntent m_textSelectionIntent;
};
class AXAttributeCacheEnabler
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) { }
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
}
--- /dev/null
+/*
+ * 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
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();
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;
}
#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
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; }
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
{
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));
}
}
virtual ~AccessibilityScrollView();
virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ AccessibilityObject* webAreaObject() const;
+
private:
explicit AccessibilityScrollView(ScrollView*);
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*);
// 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;
}
[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)
#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 {
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)
#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)
- (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;
+ (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];
}
}
@interface WebAccessibilityObjectWrapper : WebAccessibilityObjectWrapperBase
+- (id)textMarkerRangeFromVisiblePositions:(const WebCore::VisiblePosition&)startPosition endPosition:(const WebCore::VisiblePosition&)endPosition;
+- (id)textMarkerForVisiblePosition:(const WebCore::VisiblePosition&)visiblePos;
+
@end
#endif // WebAccessibilityObjectWrapper_h
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);
}
#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)
{
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()
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);
}
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;
}
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())
}
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)
}
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())
}
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())
}
}
-EditAction ApplyStyleCommand::editingAction() const
-{
- return m_editingAction;
-}
-
void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
{
// update document layout once before removing styles
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;
Position endPosition();
RefPtr<EditingStyle> m_style;
- EditAction m_editingAction;
EPropertyLevel m_propertyLevel;
Position m_start;
Position m_end;
#include "RenderBlockFlow.h"
#include "RenderText.h"
#include "RenderedDocumentMarker.h"
+#include "ReplaceDeleteFromTextNodeCommand.h"
+#include "ReplaceInsertIntoTextNodeCommand.h"
#include "ReplaceNodeWithSpanCommand.h"
#include "ReplaceSelectionCommand.h"
#include "ScopedEventQueue.h"
}
#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)
{
}
case EditActionSetWritingDirection:
case EditActionCut:
case EditActionUnspecified:
+ case EditActionInsert:
case EditActionDelete:
case EditActionDictation:
break;
void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
{
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea, editingAction()));
}
void CompositeEditCommand::insertLineBreak()
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)
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)
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)
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)
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());
virtual void getNodesInCommand(HashSet<Node*>&);
#endif
+ AXTextEditType unapplyEditType() const;
+
private:
EditCommandComposition(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
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
/*
- * 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
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)
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);
}
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
/*
- * 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
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;
String m_text;
};
+inline const String& DeleteFromTextNodeCommand::deletedText()
+{
+ return m_text;
+}
+
} // namespace WebCore
#endif // DeleteFromTextNodeCommand_h
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)
{
}
-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)
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.
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;
{
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());
}
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)
namespace WebCore {
typedef enum {
EditActionUnspecified,
+ EditActionInsert,
EditActionSetColor,
EditActionSetBackgroundColor,
EditActionTurnOffKerning,
#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());
EditAction EditCommand::editingAction() const
{
- return EditActionUnspecified;
+ return m_editingAction;
}
static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
}
}
+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();
}
#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
#ifndef EditCommand_h
#define EditCommand_h
+#include "AXTextStateChangeIntent.h"
#include "EditAction.h"
#include "VisibleSelection.h"
void setParent(CompositeEditCommand*);
- virtual EditAction editingAction() const;
+ EditAction editingAction() const;
const VisibleSelection& startingSelection() const { return m_startingSelection; }
const VisibleSelection& endingSelection() const { return m_endingSelection; }
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();
VisibleSelection m_startingSelection;
VisibleSelection m_endingSelection;
CompositeEditCommand* m_parent;
+ EditAction m_editingAction { EditActionUnspecified };
};
enum ShouldAssumeContentIsAlwaysEditable {
#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; }
};
#include <RemoveNodeCommand.cpp>
#include <RemoveNodePreservingChildrenCommand.cpp>
#include <RenderedPosition.cpp>
+#include <ReplaceDeleteFromTextNodeCommand.cpp>
+#include <ReplaceInsertIntoTextNodeCommand.cpp>
#include <ReplaceNodeWithSpanCommand.cpp>
#include <ReplaceSelectionCommand.cpp>
#include <SetNodeAttributeCommand.cpp>
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;
}
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()
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)
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();
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()
// 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();
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();
didWriteSelectionToPasteboard();
if (action == CutAction)
- deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
+ deleteSelectionWithSmartDelete(canSmartCopyOrDelete(), EditActionCut);
}
void Editor::paste()
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());
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());
// 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)
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())
// 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 ^):
#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();
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);
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);
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;
}
#include "config.h"
#include "FrameSelection.h"
+#include "AXObjectCache.h"
#include "CharacterData.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
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)
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)
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)
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.
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;
if (frameView && frameView->layoutPending())
return;
- updateAndRevealSelection();
+ updateAndRevealSelection(intent);
}
static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame)
view->clearSelection();
}
-void FrameSelection::updateAndRevealSelection()
+void FrameSelection::updateAndRevealSelection(AXTextStateChangeIntent intent)
{
if (!m_pendingSelectionUpdate)
return;
revealSelection(alignment, RevealExtent);
}
- notifyAccessibilityForSelectionChange();
+ notifyAccessibilityForSelectionChange(intent);
if (auto* client = m_frame->editor().client())
client->didChangeSelectionAndUpdateLayout();
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)
{
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
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)
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());
#ifndef FrameSelection_h
#define FrameSelection_h
+#include "AXTextStateChangeIntent.h"
#include "EditingStyle.h"
#include "IntRect.h"
#include "LayoutRect.h"
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();
private:
enum EPositionType { START, END, BASE, EXTENT };
- void updateAndRevealSelection();
+ void updateAndRevealSelection(AXTextStateChangeIntent = AXTextStateChangeIntent());
void updateDataDetectorsForSelection();
bool setSelectionWithoutUpdatingAppearance(const VisibleSelection&, SetSelectionOptions, CursorAlignOnScroll, TextGranularity);
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);
#if !(PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(EFL))
#if HAVE(ACCESSIBILITY)
-inline void FrameSelection::notifyAccessibilityForSelectionChange()
+inline void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent)
{
}
#endif
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)
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)
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);
}
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)
String m_text;
};
+inline const String& InsertIntoTextNodeCommand::insertedText()
+{
+ return m_text;
+}
+
} // namespace WebCore
#endif // InsertIntoTextNodeCommand_h
#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)
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()
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);
}
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;
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)
{
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();
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)
};
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();
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
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)
{
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();
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
#include "RenderInline.h"
#include "RenderObject.h"
#include "RenderText.h"
+#include "ReplaceDeleteFromTextNodeCommand.h"
+#include "ReplaceInsertIntoTextNodeCommand.h"
#include "SimplifyMarkupCommand.h"
#include "SmartReplace.h"
#include "StyleProperties.h"
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)
}
}
-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)
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*);
RefPtr<DocumentFragment> m_documentFragment;
bool m_preventNesting;
bool m_movingParagraph;
- EditAction m_editAction;
bool m_sanitizeFragment;
bool m_shouldMergeEnd;
bool m_ignoreMailBlockquote;
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());
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionTyping));
typingAddedToOpenCommand(InsertParagraphSeparator);
}
}
-void FrameSelection::notifyAccessibilityForSelectionChange()
+void FrameSelection::notifyAccessibilityForSelectionChange(AXTextStateChangeIntent)
{
if (!AXObjectCache::accessibilityEnabled())
return;
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))
{
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;
#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)
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);
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 {
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)
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);
+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.
// 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");
+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
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:
+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
- (void)setPlatformElement:(id)platformElement;
- (void)setCallback:(JSObjectRef)callback;
- (void)startObserving;
+- (void)stopObserving;
@end
[[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, ¬ificationNameArgument, 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);
}
}
// 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;
}
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, ¬ificationNameArgument, 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);
}
}
// 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;