Add accessibilityInsertText for text insertion in edit fields.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Jul 2019 22:04:10 +0000 (22:04 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Jul 2019 22:04:10 +0000 (22:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199973

Patch by Andres Gonzalez <andresg_22@apple.com> on 2019-07-20
Reviewed by Chris Fleizach.

Source/WebCore:

Tests: accessibility/insert-newline.html
       accessibility/ios-simulator/insert-newline.html

Accessibility clients like VoiceOver and Voice Control were entering
text in text fields by replacing the entire content of the field
(SetValue) and then setting the insertion point to the appropriate
offset (SetSelectedTextRange). accessibilityInsertText gives a simpler
interface to clients to insert text at the insertion point location.
In addition, this provides a workaround for the issue encountered with
the previous method when inserting a linebreak.

* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::insertText):
* accessibility/AccessibilityObject.h:
* accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
(-[WebAccessibilityObjectWrapper accessibilityInsertText:]):
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper accessibilityInsertText:]):

Tools:

Glue code to run new LayoutTests.
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
* WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
* WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp:
(WTR::AccessibilityUIElement::insertText):
* WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
(WTR::AccessibilityUIElement::insertText):
* WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::insertText):
* WebKitTestRunner/InjectedBundle/win/AccessibilityUIElementWin.cpp:
(WTR::AccessibilityUIElement::insertText):

LayoutTests:

Tests for inserting a newline in the middle of a text line and checking
that the insertion point and text ranges are correct.
* accessibility/insert-newline-expected.txt: Added.
* accessibility/insert-newline.html: Added.
* accessibility/ios-simulator/insert-newline-expected.txt: Added.
* accessibility/ios-simulator/insert-newline.html: Added.

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

21 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/insert-newline-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/insert-newline.html [new file with mode: 0644]
LayoutTests/platform/ios-simulator/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
Tools/ChangeLog
Tools/DumpRenderTree/AccessibilityUIElement.cpp
Tools/DumpRenderTree/AccessibilityUIElement.h
Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm
Tools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl
Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm
Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm
Tools/WebKitTestRunner/InjectedBundle/win/AccessibilityUIElementWin.cpp

index d62c461..1f7cfba 100644 (file)
@@ -1,3 +1,17 @@
+2019-07-20  Andres Gonzalez  <andresg_22@apple.com>
+
+        Add accessibilityInsertText for text insertion in edit fields.
+        https://bugs.webkit.org/show_bug.cgi?id=199973
+
+        Reviewed by Chris Fleizach.
+
+        Tests for inserting a newline in the middle of a text line and checking
+        that the insertion point and text ranges are correct.
+        * accessibility/insert-newline-expected.txt: Added.
+        * accessibility/insert-newline.html: Added.
+        * accessibility/ios-simulator/insert-newline-expected.txt: Added.
+        * accessibility/ios-simulator/insert-newline.html: Added.
+
 2019-07-20  Saam Barati  <sbarati@apple.com>
 
         [WHLSL] Make enums work
diff --git a/LayoutTests/accessibility/insert-newline-expected.txt b/LayoutTests/accessibility/insert-newline-expected.txt
new file mode 100644 (file)
index 0000000..10d4527
--- /dev/null
@@ -0,0 +1,10 @@
+hello
+world
+PASS text.selectedTextRange became '{5, 0}'
+There must be only one [newline] between hello and world: hello[newline]world
+PASS text.selectedTextRange became '{6, 0}'
+The text after the newline should be world: world
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/insert-newline.html b/LayoutTests/accessibility/insert-newline.html
new file mode 100644 (file)
index 0000000..9b7e8be
--- /dev/null
@@ -0,0 +1,39 @@
+<html>
+<head>
+<script src="../resources/js-test-pre.js"></script>
+</head>
+<body>
+
+<div id="content" contenteditable tabindex="0">helloworld</div>
+
+<div id="console"></div>
+
+<script>
+    if (window.accessibilityController) {
+        window.jsTestIsAsync = true;
+
+        var content = document.getElementById("content");
+        content.focus();
+
+        var text = accessibilityController.focusedElement;
+        text.setSelectedTextRange(5, 0);
+        shouldBecomeEqual("text.selectedTextRange", "'{5, 0}'", function() {
+            text.insertText("\n");
+
+            var t = text.stringForRange(0, 11);
+            t = t.replace(/(?:\r\n|\r|\n)/g, '[newline]');
+            debug("There must be only one [newline] between hello and world: " + t);
+
+            shouldBecomeEqual("text.selectedTextRange", "'{6, 0}'", function() {
+                var t = text.stringForRange(6, 5);
+                t = t.replace(/(?:\r\n|\r|\n)/g, '[newline]');
+                debug("The text after the newline should be world: " + t);
+
+                finishJSTest();
+            });
+        });
+    }
+</script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
index 0408652..55e2167 100644 (file)
@@ -3,6 +3,7 @@
 # See http://trac.webkit.org/wiki/TestExpectations for more information on this file.
 #
 
+accessibility/insert-newline.html [ Pass ]
 css3/font-variant-small-caps-synthesis-coverage.html [ ImageOnlyFailure ]
 fast/frames/sandboxed-iframe-close-top-noclose.html [ Failure ]
 fast/multicol/flexbox-rows.html [ Skip ]
index 79d02f7..d50ab73 100644 (file)
@@ -1698,6 +1698,9 @@ webkit.org/b/153292 accessibility/text-marker [ Skip ]
 
 webkit.org/b/184676 accessibility/ARIA-reflection.html [ Skip ]
 
+# Not implemented on Win
+accessibility/insert-newline.html [ Skip ]
+
 ################################################################################
 #######################   End Accessibility Issues   ###########################
 ################################################################################
index 44de69f..2c45bf5 100644 (file)
@@ -1,3 +1,29 @@
+2019-07-20  Andres Gonzalez  <andresg_22@apple.com>
+
+        Add accessibilityInsertText for text insertion in edit fields.
+        https://bugs.webkit.org/show_bug.cgi?id=199973
+
+        Reviewed by Chris Fleizach.
+
+        Tests: accessibility/insert-newline.html
+               accessibility/ios-simulator/insert-newline.html
+
+        Accessibility clients like VoiceOver and Voice Control were entering
+        text in text fields by replacing the entire content of the field
+        (SetValue) and then setting the insertion point to the appropriate
+        offset (SetSelectedTextRange). accessibilityInsertText gives a simpler
+        interface to clients to insert text at the insertion point location.
+        In addition, this provides a workaround for the issue encountered with
+        the previous method when inserting a linebreak.
+
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::insertText):
+        * accessibility/AccessibilityObject.h:
+        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
+        (-[WebAccessibilityObjectWrapper accessibilityInsertText:]):
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (-[WebAccessibilityObjectWrapper accessibilityInsertText:]):
+
 2019-07-20  Alexander Mikhaylenko  <exalm7659@gmail.com>
 
         REGRESSION(r246033/r246496): [GTK] Kinetic scrolling doesn't work
index 98e2240..eaf24d2 100644 (file)
@@ -2277,6 +2277,22 @@ bool AccessibilityObject::replaceTextInRange(const String& replacementString, co
     return false;
 }
 
+bool AccessibilityObject::insertText(const String& text)
+{
+    if (!renderer() || !is<Element>(node()))
+        return false;
+
+    auto& element = downcast<Element>(*renderer()->node());
+
+    // Only try to insert text if the field is in editing mode.
+    if (!element.shouldUseInputMethod())
+        return false;
+
+    // Use Editor::insertText to mimic typing into the field.
+    auto& editor = renderer()->frame().editor();
+    return editor.insertText(text, nullptr);
+}
+
 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
 AccessibilityOrientation AccessibilityObject::orientation() const
 {
index a009ec8..9fab2ad 100644 (file)
@@ -714,6 +714,7 @@ public:
     virtual void setSelectedTextRange(const PlainTextRange&) { }
     virtual void setValue(const String&) { }
     bool replaceTextInRange(const String&, const PlainTextRange&);
+    bool insertText(const String&);
 
     virtual void setValue(float) { }
     virtual void setSelected(bool) { }
index 63ab042..35007ce 100644 (file)
@@ -2561,6 +2561,14 @@ static void AXAttributedStringAppendText(NSMutableAttributedString* attrString,
     return m_object->replaceTextInRange(string, PlainTextRange(range));
 }
 
+- (BOOL)accessibilityInsertText:(NSString *)text
+{
+    if (![self _prepareAccessibilityCall])
+        return NO;
+
+    return m_object->insertText(text);
+}
+
 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
 - (NSArray *)elementsForRange:(NSRange)range
 {
index 9727ac6..9a3087e 100644 (file)
@@ -3830,6 +3830,14 @@ IGNORE_WARNINGS_END
     return m_object->replaceTextInRange(string, PlainTextRange(range));
 }
 
+- (BOOL)accessibilityInsertText:(NSString *)text
+{
+    if (![self updateObjectBackingStore])
+        return NO;
+
+    return m_object->insertText(text);
+}
+
 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
 IGNORE_WARNINGS_END
index 07c3199..7826119 100644 (file)
@@ -1,3 +1,22 @@
+2019-07-20  Andres Gonzalez  <andresg_22@apple.com>
+
+        Add accessibilityInsertText for text insertion in edit fields.
+        https://bugs.webkit.org/show_bug.cgi?id=199973
+
+        Reviewed by Chris Fleizach.
+
+        Glue code to run new LayoutTests.
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
+        * WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
+        * WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp:
+        (WTR::AccessibilityUIElement::insertText):
+        * WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
+        (WTR::AccessibilityUIElement::insertText):
+        * WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
+        (WTR::AccessibilityUIElement::insertText):
+        * WebKitTestRunner/InjectedBundle/win/AccessibilityUIElementWin.cpp:
+        (WTR::AccessibilityUIElement::insertText):
+
 2019-07-19  Zhifei Fang  <zhifei_fang@apple.com>
 
         Move webkit.css to opensource to support resultsdbpy's frontend
index 6b8736e..dece230 100644 (file)
@@ -836,6 +836,15 @@ static JSValueRef replaceTextInRangeCallback(JSContextRef context, JSObjectRef f
     return JSValueMakeBoolean(context, toAXElement(thisObject)->replaceTextInRange(text.get(), position, length));
 }
 
+static JSValueRef insertTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    if (!argumentCount)
+        return JSValueMakeUndefined(context);
+
+    auto text = adopt(JSValueToStringCopy(context, arguments[0], exception));
+    return JSValueMakeBoolean(context, toAXElement(thisObject)->insertText(text.get()));
+}
+
 static JSValueRef attributedStringForTextMarkerRangeContainsAttributeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     AccessibilityTextMarkerRange* markerRange = 0;
@@ -1649,6 +1658,11 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int)
     return false;
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef)
+{
+    return false;
+}
+
 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange*)
 {
     return 0;
@@ -1976,6 +1990,7 @@ JSClassRef AccessibilityUIElement::getJSClass()
         { "selectedTextMarkerRange", selectedTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "resetSelectedTextMarkerRange", resetSelectedTextMarkerRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "replaceTextInRange", replaceTextInRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "insertText", insertTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "attributedStringForTextMarkerRangeContainsAttribute", attributedStringForTextMarkerRangeContainsAttributeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "indexForTextMarker", indexForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "isTextMarkerValid", isTextMarkerValidCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
index 3ee21b6..3c0dc9d 100644 (file)
@@ -283,6 +283,7 @@ public:
     void resetSelectedTextMarkerRange();
     bool setSelectedVisibleTextRange(AccessibilityTextMarkerRange*);
     bool replaceTextInRange(JSStringRef, int position, int length);
+    bool insertText(JSStringRef);
 
     JSRetainPtr<JSStringRef> stringForTextMarkerRange(AccessibilityTextMarkerRange*);
     JSRetainPtr<JSStringRef> attributedStringForTextMarkerRange(AccessibilityTextMarkerRange*);
index b5367ab..7482473 100644 (file)
@@ -75,6 +75,7 @@ AccessibilityUIElement::~AccessibilityUIElement()
 - (NSAttributedString *)attributedStringForElement;
 - (NSArray *)elementsForRange:(NSRange)range;
 - (NSString *)selectionRangeString;
+- (BOOL)accessibilityInsertText:(NSString *)text;
 - (CGPoint)accessibilityClickPoint;
 - (void)accessibilityModifySelection:(WebCore::TextGranularity)granularity increase:(BOOL)increase;
 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context;
@@ -484,6 +485,11 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int)
     return false;
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef text)
+{
+    return [m_element accessibilityInsertText:[NSString stringWithJSStringRef:text]];
+}
+
 void AccessibilityUIElement::resetSelectedTextMarkerRange()
 {
 }
index 93cfe73..a879ed9 100644 (file)
@@ -71,6 +71,7 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification,
 
 @interface NSObject (WebKitAccessibilityAdditions)
 - (BOOL)accessibilityReplaceRange:(NSRange)range withText:(NSString *)string;
+- (BOOL)accessibilityInsertText:(NSString *)text;
 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
 - (NSUInteger)accessibilityIndexOfChild:(id)child;
 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
@@ -1716,6 +1717,14 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef string, int location
     return false;
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef text)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    return [m_element accessibilityInsertText:[NSString stringWithJSStringRef:text]];
+    END_AX_OBJC_EXCEPTIONS
+    return false;
+}
+
 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
 {
     BEGIN_AX_OBJC_EXCEPTIONS
index 94c21a0..36139fc 100644 (file)
@@ -282,6 +282,7 @@ public:
     RefPtr<AccessibilityTextMarkerRange> selectedTextMarkerRange();
     void resetSelectedTextMarkerRange();
     bool replaceTextInRange(JSStringRef, int position, int length);
+    bool insertText(JSStringRef);
     RefPtr<AccessibilityTextMarker> startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
     RefPtr<AccessibilityTextMarker> endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange*);
     RefPtr<AccessibilityTextMarker> endTextMarkerForBounds(int x, int y, int width, int height);
index 1c2bd46..40f25a9 100644 (file)
     AccessibilityTextMarkerRange selectedTextMarkerRange();
     void resetSelectedTextMarkerRange();
     boolean replaceTextInRange(DOMString string, long position, long length);
+    boolean insertText(DOMString text);
     AccessibilityTextMarker startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange range);
     AccessibilityTextMarker endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange range);
     AccessibilityTextMarker endTextMarkerForBounds(long x, long y, long width, long height);
index 40e6b33..0e5c07c 100644 (file)
@@ -2332,6 +2332,12 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int)
     return false;
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef)
+{
+    notImplemented();
+    return false;
+}
+
 JSRetainPtr<JSStringRef> AccessibilityUIElement::popupValue() const
 {
     notImplemented();
index 2700807..85d5bb6 100644 (file)
@@ -58,6 +58,7 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification,
 - (NSRange)_accessibilitySelectedTextRange;
 - (void)_accessibilitySetSelectedTextRange:(NSRange)range;
 - (BOOL)accessibilityReplaceRange:(NSRange)range withText:(NSString *)string;
+- (BOOL)accessibilityInsertText:(NSString *)text;
 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context;
 - (CGFloat)_accessibilityMinValue;
 - (CGFloat)_accessibilityMaxValue;
@@ -1163,6 +1164,11 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef string, int location
     return [m_element accessibilityReplaceRange:NSMakeRange(location, length) withText:[NSString stringWithJSStringRef:string]];
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef text)
+{
+    return [m_element accessibilityInsertText:[NSString stringWithJSStringRef:text]];
+}
+
 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
 {
     return nullptr;
index 8958b63..c27377c 100644 (file)
@@ -75,6 +75,7 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification,
 
 @interface NSObject (WebKitAccessibilityAdditions)
 - (BOOL)accessibilityReplaceRange:(NSRange)range withText:(NSString *)string;
+- (BOOL)accessibilityInsertText:(NSString *)text;
 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
 - (NSUInteger)accessibilityIndexOfChild:(id)child;
 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
@@ -1876,7 +1877,15 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef string, int location
     END_AX_OBJC_EXCEPTIONS
     return false;
 }
-    
+
+bool AccessibilityUIElement::insertText(JSStringRef text)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    return [m_element accessibilityInsertText:[NSString stringWithJSStringRef:text]];
+    END_AX_OBJC_EXCEPTIONS
+    return false;
+}
+
 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
 {
     BEGIN_AX_OBJC_EXCEPTIONS
index c341857..979ca95 100644 (file)
@@ -991,6 +991,12 @@ bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int)
     return false;
 }
 
+bool AccessibilityUIElement::insertText(JSStringRef)
+{
+    notImplemented();
+    return false;
+}
+
 } // namespace  WTF
 
 #endif // HAVE(ACCESSIBILITY)