Skip to content

Commit

Permalink
Bug 1780713: Fix a11y exposure of checkable XUL buttons/toolbarbutton…
Browse files Browse the repository at this point in the history
…s. r=eeejay

1. Expose role TOGGLE_BUTTON without the CHECKABLE state.
2. Fire a state change event when the button is toggled.

Differential Revision: https://phabricator.services.mozilla.com/D152486
  • Loading branch information
jcsteh committed Jul 27, 2022
1 parent 49f84bd commit 349756f
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 11 deletions.
2 changes: 1 addition & 1 deletion accessible/tests/mochitest/events/a11y.ini
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ skip-if = os == 'mac'
skip-if = os == 'mac'
[test_selection_aria.html]
[test_statechange.html]
[test_statechange_tabpanels.xhtml]
[test_statechange.xhtml]
[test_stylechange.html]
[test_text.html]
[test_text_alg.html]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="tabpanels state change event tests">
title="XUL state change event tests">

<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
Expand Down Expand Up @@ -32,7 +32,7 @@
];
}

async function doTests() {
async function testTabpanels() {
const tabs = getNode("tabs");
is(tabs.selectedIndex, 0, "tab1 initially selected");
const panel1 = getAccessible("panel1");
Expand Down Expand Up @@ -65,7 +65,24 @@
info("Selecting tab1");
tabs.selectedIndex = 0;
await events;
}

async function testPressed() {
const toolbarbuttonCheckbox = getNode("toolbarbuttonCheckbox");
testStates(toolbarbuttonCheckbox, 0, 0, STATE_PRESSED);
info("Checking toolbarbuttonCheckbox");
let changed = waitForStateChange(toolbarbuttonCheckbox, STATE_PRESSED, true);
toolbarbuttonCheckbox.setAttribute("checked", true);
await changed;
info("Unchecking toolbarbuttonCheckbox");
changed = waitForStateChange(toolbarbuttonCheckbox, STATE_PRESSED, false);
toolbarbuttonCheckbox.removeAttribute("checked");
await changed;
}

async function doTests() {
await testTabpanels();
await testPressed();
SimpleTest.finish();
}

Expand Down Expand Up @@ -94,5 +111,7 @@
<hbox id="panel3"><button label="b3"/></hbox>
</tabpanels>
</tabbox>

<toolbarbutton id="toolbarbuttonCheckbox" type="checkbox">toolbarbuttonCheckbox</toolbarbutton>
</hbox>
</window>
2 changes: 1 addition & 1 deletion accessible/tests/mochitest/states/test_controls.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
testStates("button", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
testStates("button-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
testStates("checkbutton",
STATE_FOCUSABLE | STATE_CHECKABLE | STATE_PRESSED);
STATE_FOCUSABLE | STATE_PRESSED, 0, STATE_CHECKABLE);
testStates("fakecheckbutton", STATE_FOCUSABLE | STATE_PRESSED, 0,
STATE_CHECKABLE);
testStates("combobox", STATE_FOCUSABLE | STATE_HASPOPUP, 0, STATE_UNAVAILABLE);
Expand Down
11 changes: 11 additions & 0 deletions accessible/tests/mochitest/tree/test_button.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
};
testAccessibleTree("button2", accTree);

//////////////////////////////////////////////////////////////////////////
// toolbarbutton with type="checkbox"

accTree = {
role: ROLE_TOGGLE_BUTTON,
name: "hello",
children: [ ]
};
testAccessibleTree("button3", accTree);

SimpleTest.finish()
}

Expand All @@ -65,6 +75,7 @@
<vbox flex="1">
<button id="button1" label="hello"/>
<toolbarbutton id="button2" label="hello"/>
<toolbarbutton id="button3" type="checkbox" label="hello"/>
</vbox>
</hbox>

Expand Down
27 changes: 20 additions & 7 deletions accessible/xul/XULFormControlAccessible.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,28 @@ void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
////////////////////////////////////////////////////////////////////////////////
// XULButtonAccessible: LocalAccessible

role XULButtonAccessible::NativeRole() const { return roles::PUSHBUTTON; }
role XULButtonAccessible::NativeRole() const {
// Buttons can be checked; they simply appear pressed in rather than checked.
// In this case, we must expose them as toggle buttons.
nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
if (xulButtonElement) {
nsAutoString type;
xulButtonElement->GetType(type);
if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
return roles::TOGGLE_BUTTON;
}
}
return roles::PUSHBUTTON;
}

uint64_t XULButtonAccessible::NativeState() const {
// Possible states: focused, focusable, unavailable(disabled).

// get focus and disable status from base class
uint64_t state = LocalAccessible::NativeState();

// Buttons can be checked -- they simply appear pressed in rather than checked
nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
if (xulButtonElement) {
nsAutoString type;
xulButtonElement->GetType(type);
if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
state |= states::CHECKABLE;
}
// Some buttons can have their checked state set without being of type
// checkbox or radio. Expose the pressed state unconditionally.
bool checked = false;
Expand All @@ -92,6 +98,13 @@ uint64_t XULButtonAccessible::NativeState() const {
return state;
}

bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
if (aAttribute == nsGkAtoms::checked) {
return true;
}
return AccessibleWrap::AttributeChangesState(aAttribute);
}

////////////////////////////////////////////////////////////////////////////////
// XULButtonAccessible: Widgets

Expand Down
1 change: 1 addition & 0 deletions accessible/xul/XULFormControlAccessible.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class XULButtonAccessible : public AccessibleWrap {
// LocalAccessible
virtual mozilla::a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;
virtual bool AttributeChangesState(nsAtom* aAttribute) override;

// ActionAccessible
virtual bool HasPrimaryAction() const override;
Expand Down

0 comments on commit 349756f

Please sign in to comment.