Commit 835fa233 authored by Alice Boxhall's avatar Alice Boxhall Committed by Commit Bot

Fire synthesised keydown/keyup events for AT increment/decrement.

This is a proof of concept at this stage.

Written cooperatively with meredithl@ and chrishall@

Bug: 1099069
Change-Id: Ib2c7cf94145bf7080973f7f619cc60dc89124931
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2264176
Commit-Queue: Alice Boxhall <aboxhall@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#784271}
parent 1c3d29fa
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <algorithm> #include <algorithm>
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/strings/grit/blink_strings.h" #include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
#include "third_party/blink/renderer/core/aom/accessible_node.h" #include "third_party/blink/renderer/core/aom/accessible_node.h"
...@@ -47,6 +48,7 @@ ...@@ -47,6 +48,7 @@
#include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/editing/position.h" #include "third_party/blink/renderer/core/editing/position.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h" #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
...@@ -100,9 +102,12 @@ ...@@ -100,9 +102,12 @@
#include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h" #include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
#include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h" #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
#include "third_party/blink/renderer/platform/graphics/image_data_buffer.h" #include "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h" #include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
namespace { namespace {
bool IsNeutralWithinTable(blink::AXObject* obj) { bool IsNeutralWithinTable(blink::AXObject* obj) {
...@@ -114,6 +119,38 @@ bool IsNeutralWithinTable(blink::AXObject* obj) { ...@@ -114,6 +119,38 @@ bool IsNeutralWithinTable(blink::AXObject* obj) {
role == ax::mojom::blink::Role::kIgnored || role == ax::mojom::blink::Role::kIgnored ||
role == ax::mojom::blink::Role::kRowGroup; role == ax::mojom::blink::Role::kRowGroup;
} }
enum class AXAction {
kActionIncrement = 0,
kActionDecrement,
};
blink::KeyboardEvent* CreateKeyboardEvent(
blink::LocalDOMWindow* local_dom_window,
blink::WebInputEvent::Type type,
AXAction action) {
blink::WebKeyboardEvent key(type,
blink::WebInputEvent::Modifiers::kNoModifiers,
base::TimeTicks::Now());
// TODO(crbug.com/1099069): Fire different arrow events depending on
// orientation and dir (RTL/LTR)
switch (action) {
case AXAction::kActionIncrement:
key.dom_key = ui::DomKey::ARROW_UP;
key.dom_code = static_cast<int>(ui::DomCode::ARROW_UP);
key.native_key_code = key.windows_key_code = blink::VKEY_UP;
break;
case AXAction::kActionDecrement:
key.dom_key = ui::DomKey::ARROW_DOWN;
key.dom_code = static_cast<int>(ui::DomCode::ARROW_DOWN);
key.native_key_code = key.windows_key_code = blink::VKEY_DOWN;
break;
}
return blink::KeyboardEvent::Create(key, local_dom_window, true);
}
} // namespace } // namespace
namespace blink { namespace blink {
...@@ -137,6 +174,8 @@ AXNodeObject::~AXNodeObject() { ...@@ -137,6 +174,8 @@ AXNodeObject::~AXNodeObject() {
} }
void AXNodeObject::AlterSliderOrSpinButtonValue(bool increase) { void AXNodeObject::AlterSliderOrSpinButtonValue(bool increase) {
if (!GetNode())
return;
if (!IsSlider() && !IsSpinButton()) if (!IsSlider() && !IsSpinButton())
return; return;
...@@ -150,15 +189,39 @@ void AXNodeObject::AlterSliderOrSpinButtonValue(bool increase) { ...@@ -150,15 +189,39 @@ void AXNodeObject::AlterSliderOrSpinButtonValue(bool increase) {
value += increase ? step : -step; value += increase ? step : -step;
OnNativeSetValueAction(String::Number(value)); // If this is a native element, set the value directly.
if (native_role_ == ax::mojom::blink::Role::kSlider ||
native_role_ == ax::mojom::blink::Role::kSpinButton) {
OnNativeSetValueAction(String::Number(value));
// Dispatching an event could result in changes to the document, like
// this AXObject becoming detached.
if (IsDetached())
return;
// Dispatching an event could result in changes to the document, like AXObjectCache().PostNotification(GetNode(),
// this AXObject becoming detached. ax::mojom::blink::Event::kValueChanged);
if (IsDetached())
return; return;
}
// TODO(crbug.com/1099069): add a separate flag for keyboard event synthesis
if (!RuntimeEnabledFeatures::AccessibilityObjectModelEnabled())
return;
// Otherwise, fire a keyboard event instead.
AXAction action =
increase ? AXAction::kActionIncrement : AXAction::kActionDecrement;
LocalDOMWindow* local_dom_window = GetDocument()->domWindow();
KeyboardEvent* keydown = CreateKeyboardEvent(
local_dom_window, WebInputEvent::Type::kRawKeyDown, action);
GetNode()->DispatchEvent(*keydown);
// TODO(crbug.com/1099069): add a brief pause between keydown and keyup?
// TODO(crbug.com/1099069): fire a "char" event depending on platform?
AXObjectCache().PostNotification(GetNode(), KeyboardEvent* keyup = CreateKeyboardEvent(
ax::mojom::blink::Event::kValueChanged); local_dom_window, WebInputEvent::Type::kKeyUp, action);
GetNode()->DispatchEvent(*keyup);
} }
AXObject* AXNodeObject::ActiveDescendant() { AXObject* AXNodeObject::ActiveDescendant() {
......
...@@ -41,10 +41,12 @@ AXSlider::AXSlider(LayoutObject* layout_object, ...@@ -41,10 +41,12 @@ AXSlider::AXSlider(LayoutObject* layout_object,
: AXLayoutObject(layout_object, ax_object_cache) {} : AXLayoutObject(layout_object, ax_object_cache) {}
ax::mojom::Role AXSlider::DetermineAccessibilityRole() { ax::mojom::Role AXSlider::DetermineAccessibilityRole() {
native_role_ = ax::mojom::blink::Role::kSlider;
if ((aria_role_ = DetermineAriaRoleAttribute()) != ax::mojom::Role::kUnknown) if ((aria_role_ = DetermineAriaRoleAttribute()) != ax::mojom::Role::kUnknown)
return aria_role_; return aria_role_;
return ax::mojom::Role::kSlider; return native_role_;
} }
AccessibilityOrientation AXSlider::Orientation() const { AccessibilityOrientation AXSlider::Orientation() const {
......
<!DOCTYPE HTML>
<html>
<head>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
</head>
<body>
<div id="slider"
role="slider"
aria-valuemin="0"
aria-valuemax="10"
aria-valuenow="5"></div>
<script>
function checkEvent(event, expected_key) {
if (expected_key == "ArrowUp") {
assert_equals(event.code, "ArrowUp", "event.code on " + event.type);
assert_equals(event.key, "ArrowUp", "event.key on " + event.type);
assert_equals(event.keyCode, 38, "event.keyCode on " + event.type);
assert_equals(event.which, 38, "event.which on " + event.type);
} else if (expected_key == "ArrowDown") {
assert_equals(event.code, "ArrowDown", "event.code on " + event.type);
assert_equals(event.key, "ArrowDown", "event.key on " + event.type);
assert_equals(event.keyCode, 40, "event.keyCode on " + event.type);
assert_equals(event.which, 40, "event.which on " + event.type);
}
assert_true(event.isTrusted, "event.isTrusted on " + event.type);
assert_equals(event.charCode, 0, "event.charCode on " + event.type);
assert_equals(event.target, slider, "event.target on " + event.type);
assert_equals(event.srcElement, slider, "event.srcElement on " + event.type);
assert_true(event.bubbles, "event.bubbles on " + event.type);
assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
}
async_test(function(t) {
var slider = document.getElementById("slider");
var axSlider = accessibilityController.accessibleElementById("slider");
slider.addEventListener("keydown", t.step_func((event) => {
checkEvent(event, "ArrowUp");
}), { once: true });
slider.addEventListener("keyup", t.step_func((event) => {
checkEvent(event, "ArrowUp");
slider.addEventListener("keydown", t.step_func((event) => {
checkEvent(event, "ArrowDown");
}), { once: true });
slider.addEventListener("keyup", t.step_func_done((event) => {
checkEvent(event, "ArrowDown");
t.done();
}), { once: true });
window.setTimeout(() => {
axSlider.decrement();
}, 0);
}), { once: true });
window.setTimeout(t.step_func(() => {
assert(false, "didn't get all key events within 1000ms");
t.done();
}), 1000);
axSlider.increment();
}, "check that sending an increment event to an ARIA slider generates an up arrow key event");
</script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment