AX: Provide API for assistive tech to ignore DOM key event handlers
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Mar 2015 19:24:01 +0000 (19:24 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Mar 2015 19:24:01 +0000 (19:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142059

Patch by Doug Russell <d_russell@apple.com> on 2015-03-13
Reviewed by Beth Dakin.

Assistive technology applications on the desktop are heavily dependent on keyboard navigation being reliable. This is greatly hindered by sites that handle key events without updating keyboard selection and then consume the event. It is important for assistive technology apps to allow users to decide to ignore these handlers that are incorrect for their purposes.

This can be fixed by exposing, via a new accessibility attribute, a way to decide, for a given WebCore::Frame, to pre-empt DOM dispatch and instead let accessibility caret browsing take place.

Source/WebCore:

Test: platform/mac/accessibility/prevent-keyboard-event-dispatch.html

* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::preventKeyboardDOMEventDispatch):
(WebCore::AccessibilityObject::setPreventKeyboardDOMEventDispatch):
* accessibility/AccessibilityObject.h:
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper accessibilityAttributeNames]):
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
(-[WebAccessibilityObjectWrapper accessibilityIsAttributeSettable:]):
(-[WebAccessibilityObjectWrapper _accessibilitySetValue:forAttribute:]):
* dom/Element.cpp:
(WebCore::Element::dispatchKeyEvent):
* page/EventHandler.cpp:
(WebCore::EventHandler::keyEvent):
(WebCore::handleKeyboardSelectionMovement):
(WebCore::EventHandler::handleKeyboardSelectionMovementForAccessibility):
* page/EventHandler.h:
* page/Settings.in:

LayoutTests:

* accessibility/parent-delete-expected.txt:
* platform/mac/accessibility/document-attributes-expected.txt:
* platform/mac/accessibility/prevent-keyboard-event-dispatch-expected.txt: Added.
* platform/mac/accessibility/prevent-keyboard-event-dispatch.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/parent-delete-expected.txt
LayoutTests/platform/mac/accessibility/document-attributes-expected.txt
LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
Source/WebCore/dom/Element.cpp
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h
Source/WebCore/page/Settings.in

index c289d2f..bbd4678 100644 (file)
@@ -1,3 +1,19 @@
+2015-03-13  Doug Russell  <d_russell@apple.com>
+
+        AX: Provide API for assistive tech to ignore DOM key event handlers
+        https://bugs.webkit.org/show_bug.cgi?id=142059
+
+        Reviewed by Beth Dakin.
+
+        Assistive technology applications on the desktop are heavily dependent on keyboard navigation being reliable. This is greatly hindered by sites that handle key events without updating keyboard selection and then consume the event. It is important for assistive technology apps to allow users to decide to ignore these handlers that are incorrect for their purposes.
+
+        This can be fixed by exposing, via a new accessibility attribute, a way to decide, for a given WebCore::Frame, to pre-empt DOM dispatch and instead let accessibility caret browsing take place.
+
+        * accessibility/parent-delete-expected.txt:
+        * platform/mac/accessibility/document-attributes-expected.txt:
+        * platform/mac/accessibility/prevent-keyboard-event-dispatch-expected.txt: Added.
+        * platform/mac/accessibility/prevent-keyboard-event-dispatch.html: Added.
+
 2015-03-13  Chris Dumez  <cdumez@apple.com>
 
         XMLHttpRequests should not prevent a page from entering PageCache
index 4246754..921ab78 100644 (file)
@@ -28,5 +28,6 @@ AXLayoutCount: 2
 AXLoadingProgress: 1
 AXURL: LayoutTests/accessibility/parent-delete.html
 AXCaretBrowsingEnabled: 0
+AXPreventKeyboardDOMEventDispatch: 0
 AXElementBusy: 0
 
index 8f4bae1..b195826 100644 (file)
@@ -27,6 +27,7 @@ AXLayoutCount: 2
 AXLoadingProgress: 1
 AXURL: LayoutTests/platform/mac/accessibility/document-attributes.html
 AXCaretBrowsingEnabled: 0
+AXPreventKeyboardDOMEventDispatch: 0
 AXElementBusy: 0
 
 
diff --git a/LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch-expected.txt b/LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch-expected.txt
new file mode 100644 (file)
index 0000000..b4d6b82
--- /dev/null
@@ -0,0 +1,29 @@
+1
+
+2
+
+2
+
+This tests ignoring javascript key handlers that consume key events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS webArea.role is 'AXRole: AXWebArea'
+PASS caretBrowsingEnabled(webArea) is false
+PASS accessibilityController.enhancedAccessibilityEnabled is false
+PASS preventKeyboardDOMEventDispatch(webArea) is false
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 1'
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 1'
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 1'
+PASS keycount() is '2'
+PASS accessibilityController.enhancedAccessibilityEnabled is true
+PASS preventKeyboardDOMEventDispatch(webArea) is true
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 1'
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 1'
+PASS elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue is 'AXValue: 2'
+PASS keycount() is '2'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch.html b/LayoutTests/platform/mac/accessibility/prevent-keyboard-event-dispatch.html
new file mode 100644 (file)
index 0000000..8c58f3d
--- /dev/null
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../../../resources/js-test-pre.js"></script>
+    <script src="resources/accessibility-helper.js"></script>
+</head>
+<body id="body" onkeydown="return keydown();">
+    <script>
+    function preventKeyboardDOMEventDispatch(webArea) {
+        return webArea.boolAttributeValue("AXPreventKeyboardDOMEventDispatch");
+    }
+    function setPreventKeyboardDOMEventDispatch(webArea, value) {
+        webArea.setBoolAttributeValue("AXPreventKeyboardDOMEventDispatch", value);
+    }
+    function keydown(event) {
+        var element = document.getElementById("keydowncount");
+        element.innerHTML = parseInt(element.innerHTML) + 1;
+        return false;
+    }
+    function keycount() {
+        return document.getElementById("keydowncount").innerHTML;
+    }
+    </script>
+    <div>
+        <p>1</p>
+        <p>2</p>
+        <p id="keydowncount">0<p>
+    </div>
+    <div id="console"></div>
+    <script>
+    description("This tests ignoring javascript key handlers that consume key events.");
+    if (window.testRunner) {
+
+        testRunner.dumpAsText();
+
+        if (window.accessibilityController && window.eventSender) {
+
+            var webArea = clearSelectionAndFocusOnWebArea();
+            accessibilityController.enableEnhancedAccessibility(false);
+            shouldBe("accessibilityController.enhancedAccessibilityEnabled", "false");
+            setPreventKeyboardDOMEventDispatch(webArea, false);
+            shouldBe("preventKeyboardDOMEventDispatch(webArea)", "false");
+
+            // Arrowing before enabling AX and ignore dom handlers won't move the caret
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 1'");
+            eventSender.keyDown("rightArrow");
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 1'");
+            eventSender.keyDown("rightArrow");
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 1'");
+
+            // Validate that the handler received 2 keys events
+            shouldBe("keycount()", "'2'");
+
+            // Enable enhanced accessibility (necessary for accessibility specific selection handling).
+            accessibilityController.enableEnhancedAccessibility(true);
+            shouldBe("accessibilityController.enhancedAccessibilityEnabled", "true");
+
+            // Enable IgnoreDOMKeyEventHandlers so that the javascript handler will be skipped
+            setPreventKeyboardDOMEventDispatch(webArea, true);
+            shouldBe("preventKeyboardDOMEventDispatch(webArea)", "true");
+
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 1'");
+            eventSender.keyDown("rightArrow");
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 1'");
+            eventSender.keyDown("rightArrow");
+            shouldBe("elementAtStartMarkerOfSelectedTextMarkerRange(webArea).stringValue", "'AXValue: 2'");
+
+            // Validate that the handler didn't receive any more key events
+            shouldBe("keycount()", "'2'");
+
+            setPreventKeyboardDOMEventDispatch(webArea, false);
+        }
+    }
+    </script>
+    <script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index 5ea49e0..16af2a5 100644 (file)
@@ -1,3 +1,34 @@
+2015-03-13  Doug Russell  <d_russell@apple.com>
+
+        AX: Provide API for assistive tech to ignore DOM key event handlers
+        https://bugs.webkit.org/show_bug.cgi?id=142059
+
+        Reviewed by Beth Dakin.
+
+        Assistive technology applications on the desktop are heavily dependent on keyboard navigation being reliable. This is greatly hindered by sites that handle key events without updating keyboard selection and then consume the event. It is important for assistive technology apps to allow users to decide to ignore these handlers that are incorrect for their purposes.
+
+        This can be fixed by exposing, via a new accessibility attribute, a way to decide, for a given WebCore::Frame, to pre-empt DOM dispatch and instead let accessibility caret browsing take place.
+
+        Test: platform/mac/accessibility/prevent-keyboard-event-dispatch.html
+
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::preventKeyboardDOMEventDispatch):
+        (WebCore::AccessibilityObject::setPreventKeyboardDOMEventDispatch):
+        * accessibility/AccessibilityObject.h:
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (-[WebAccessibilityObjectWrapper accessibilityAttributeNames]):
+        (-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
+        (-[WebAccessibilityObjectWrapper accessibilityIsAttributeSettable:]):
+        (-[WebAccessibilityObjectWrapper _accessibilitySetValue:forAttribute:]):
+        * dom/Element.cpp:
+        (WebCore::Element::dispatchKeyEvent):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::keyEvent):
+        (WebCore::handleKeyboardSelectionMovement):
+        (WebCore::EventHandler::handleKeyboardSelectionMovementForAccessibility):
+        * page/EventHandler.h:
+        * page/Settings.in:
+
 2015-03-09  Conrad Shultz  <conrad_shultz@apple.com>
 
         Allow clients to selectively disable plug-ins
index 8b4915e..258bf63 100644 (file)
@@ -2605,4 +2605,20 @@ void AccessibilityObject::elementsFromAttribute(Vector<Element*>& elements, cons
     }
 }
 
+#if PLATFORM(COCOA)
+bool AccessibilityObject::preventKeyboardDOMEventDispatch() const
+{
+    Frame* frame = this->frame();
+    return frame && frame->settings().preventKeyboardDOMEventDispatch();
+}
+
+void AccessibilityObject::setPreventKeyboardDOMEventDispatch(bool on)
+{
+    Frame* frame = this->frame();
+    if (!frame)
+        return;
+    frame->settings().setPreventKeyboardDOMEventDispatch(on);
+}
+#endif
+
 } // namespace WebCore
index 176fade..04188ee 100644 (file)
@@ -978,6 +978,11 @@ public:
     // other operations update type operations
     void updateBackingStore();
     
+#if PLATFORM(COCOA)
+    bool preventKeyboardDOMEventDispatch() const;
+    void setPreventKeyboardDOMEventDispatch(bool);
+#endif
+    
 #if PLATFORM(COCOA) && !PLATFORM(IOS)
     bool caretBrowsingEnabled() const;
     void setCaretBrowsingEnabled(bool);
index 74324f0..8014365 100644 (file)
@@ -464,6 +464,10 @@ using namespace HTMLNames;
 #define NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
 #define NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
 
+#ifndef NSAccessibilityPreventKeyboardDOMEventDispatchAttribute
+#define NSAccessibilityPreventKeyboardDOMEventDispatchAttribute @"AXPreventKeyboardDOMEventDispatch"
+#endif
+
 #ifndef NSAccessibilityCaretBrowsingEnabledAttribute
 #define NSAccessibilityCaretBrowsingEnabledAttribute @"AXCaretBrowsingEnabled"
 #endif
@@ -1359,6 +1363,7 @@ static id textMarkerRangeFromVisiblePositions(AXObjectCache *cache, VisiblePosit
         [tempArray addObject:NSAccessibilityLoadingProgressAttribute];
         [tempArray addObject:NSAccessibilityURLAttribute];
         [tempArray addObject:NSAccessibilityCaretBrowsingEnabledAttribute];
+        [tempArray addObject:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute];
         webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
         [tempArray release];
     }
@@ -2958,6 +2963,9 @@ static NSString* roleValueToNSString(AccessibilityRole value)
     if ([attributeName isEqualToString:@"AXDRTElementIdAttribute"])
         return m_object->getAttribute(idAttr);
     
+    if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute])
+        return [NSNumber numberWithBool:m_object->preventKeyboardDOMEventDispatch()];
+    
     if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute])
         return [NSNumber numberWithBool:m_object->caretBrowsingEnabled()];
     
@@ -3033,6 +3041,9 @@ static NSString* roleValueToNSString(AccessibilityRole value)
     if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
         return YES;
     
+    if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute])
+        return YES;
+    
     if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute])
         return YES;
     
@@ -3352,6 +3363,8 @@ static NSString* roleValueToNSString(AccessibilityRole value)
             m_object->setSelectedRows(selectedRows);
     } else if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
         m_object->setARIAGrabbed([number boolValue]);
+    else if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute])
+        m_object->setPreventKeyboardDOMEventDispatch([number boolValue]);
     else if (m_object->isWebArea() && [attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute])
         m_object->setCaretBrowsingEnabled([number boolValue]);
 }
index 5dd97aa..222aebc 100644 (file)
@@ -39,6 +39,7 @@
 #include "ElementIterator.h"
 #include "ElementRareData.h"
 #include "EventDispatcher.h"
+#include "EventHandler.h"
 #include "FlowThreadController.h"
 #include "FocusController.h"
 #include "FocusEvent.h"
@@ -289,6 +290,10 @@ bool Element::dispatchWheelEvent(const PlatformWheelEvent& event)
 bool Element::dispatchKeyEvent(const PlatformKeyboardEvent& platformEvent)
 {
     RefPtr<KeyboardEvent> event = KeyboardEvent::create(platformEvent, document().defaultView());
+    if (Frame* frame = document().frame()) {
+        if (frame->eventHandler().accessibilityPreventsEventPropogation(event.get()))
+            event->stopPropagation();
+    }
     return EventDispatcher::dispatchEvent(this, event) && !event->defaultHandled();
 }
 
index de15d82..5647d3b 100644 (file)
@@ -3087,6 +3087,9 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
         keydown->setTarget(element);
         keydown->setDefaultHandled();
     }
+    
+    if (accessibilityPreventsEventPropogation(keydown.get()))
+        keydown->stopPropagation();
 
     element->dispatchEvent(keydown, IGNORE_EXCEPTION);
     // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
@@ -3230,6 +3233,27 @@ void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent
     }
 }
 
+bool EventHandler::accessibilityPreventsEventPropogation(KeyboardEvent* event)
+{
+#if PLATFORM(COCOA)
+    if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
+        return false;
+
+    if (!m_frame.settings().preventKeyboardDOMEventDispatch())
+        return false;
+
+    // Check for key events that are relevant to accessibility: tab and arrows keys that change focus
+    if (event->keyIdentifier() == "U+0009")
+        return true;
+    FocusDirection direction = focusDirectionForKey(event->keyIdentifier());
+    if (direction != FocusDirectionNone)
+        return true;
+#else
+    UNUSED_PARAM(event);
+#endif
+    return false;
+}
+
 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
 {
     if (event->type() == eventNames().keydownEvent) {
index 3720f6c..5df29d0 100644 (file)
@@ -239,6 +239,7 @@ public:
     WEBCORE_EXPORT bool keyEvent(const PlatformKeyboardEvent&);
     void defaultKeyboardEventHandler(KeyboardEvent*);
 
+    bool accessibilityPreventsEventPropogation(KeyboardEvent*);
     WEBCORE_EXPORT void handleKeyboardSelectionMovementForAccessibility(KeyboardEvent*);
 
     bool handleTextInputEvent(const String& text, Event* underlyingEvent = 0, TextEventInputType = TextEventInputKeyboard);
index f45314c..22d15de 100644 (file)
@@ -45,6 +45,7 @@ maximumHTMLParserDOMTreeDepth type=unsigned, initial=defaultMaximumHTMLParserDOM
 loadsSiteIconsIgnoringImageLoadingSetting initial=false
 
 caretBrowsingEnabled initial=false
+preventKeyboardDOMEventDispatch initial=false
 localStorageEnabled initial=false
 allowUniversalAccessFromFileURLs initial=true
 allowFileAccessFromFileURLs initial=true