зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1720334: Represent checked/unchecked state with AXValue for treeitems r=eeejay
Differential Revision: https://phabricator.services.mozilla.com/D121215
This commit is contained in:
Родитель
ace5987168
Коммит
224e6f9293
|
@ -111,7 +111,7 @@ using namespace mozilla::a11y;
|
||||||
static const uint64_t kCachedStates =
|
static const uint64_t kCachedStates =
|
||||||
states::CHECKED | states::PRESSED | states::MIXED | states::EXPANDED |
|
states::CHECKED | states::PRESSED | states::MIXED | states::EXPANDED |
|
||||||
states::CURRENT | states::SELECTED | states::TRAVERSED | states::LINKED |
|
states::CURRENT | states::SELECTED | states::TRAVERSED | states::LINKED |
|
||||||
states::HASPOPUP | states::BUSY | states::MULTI_LINE;
|
states::HASPOPUP | states::BUSY | states::MULTI_LINE | states::CHECKABLE;
|
||||||
static const uint64_t kCacheInitialized = ((uint64_t)0x1) << 63;
|
static const uint64_t kCacheInitialized = ((uint64_t)0x1) << 63;
|
||||||
|
|
||||||
- (uint64_t)state {
|
- (uint64_t)state {
|
||||||
|
|
|
@ -174,6 +174,9 @@
|
||||||
// override
|
// override
|
||||||
- (NSString*)moxLabel;
|
- (NSString*)moxLabel;
|
||||||
|
|
||||||
|
// override
|
||||||
|
- (id)moxValue;
|
||||||
|
|
||||||
// override
|
// override
|
||||||
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled;
|
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled;
|
||||||
|
|
||||||
|
|
|
@ -622,16 +622,53 @@ enum CachedBool { eCachedBoolMiss, eCachedTrue, eCachedFalse };
|
||||||
return nsCocoaUtils::ToNSString(title);
|
return nsCocoaUtils::ToNSString(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CheckedState {
|
||||||
|
kUncheckable = -1,
|
||||||
|
kUnchecked = 0,
|
||||||
|
kChecked = 1,
|
||||||
|
kMixed = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
- (int)checkedValue {
|
||||||
|
uint64_t state = [self
|
||||||
|
stateWithMask:(states::CHECKABLE | states::CHECKED | states::MIXED)];
|
||||||
|
|
||||||
|
if (state & states::CHECKABLE) {
|
||||||
|
if (state & states::CHECKED) {
|
||||||
|
return kChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state & states::MIXED) {
|
||||||
|
return kMixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kUnchecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kUncheckable;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)moxValue {
|
||||||
|
int checkedValue = [self checkedValue];
|
||||||
|
return checkedValue >= 0 ? @(checkedValue) : nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
|
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
|
||||||
[super stateChanged:state isEnabled:enabled];
|
[super stateChanged:state isEnabled:enabled];
|
||||||
|
|
||||||
if (state == states::EXPANDED) {
|
if (state & states::EXPANDED) {
|
||||||
// If the EXPANDED state is updated, fire appropriate events on the
|
// If the EXPANDED state is updated, fire appropriate events on the
|
||||||
// outline row.
|
// outline row.
|
||||||
[self moxPostNotification:(enabled
|
[self moxPostNotification:(enabled
|
||||||
? NSAccessibilityRowExpandedNotification
|
? NSAccessibilityRowExpandedNotification
|
||||||
: NSAccessibilityRowCollapsedNotification)];
|
: NSAccessibilityRowCollapsedNotification)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state & (states::CHECKED | states::CHECKABLE | states::MIXED)) {
|
||||||
|
// If the MIXED, CHECKED or CHECKABLE state changes, update the value we
|
||||||
|
// expose for the row, which communicates checked status.
|
||||||
|
[self moxPostNotification:NSAccessibilityValueChangedNotification];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,18 +7,6 @@
|
||||||
/* import-globals-from ../../mochitest/states.js */
|
/* import-globals-from ../../mochitest/states.js */
|
||||||
loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
|
loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
|
||||||
|
|
||||||
function waitForStateChange(id, state, isEnabled) {
|
|
||||||
return waitForEvent(EVENT_STATE_CHANGE, e => {
|
|
||||||
e.QueryInterface(nsIAccessibleStateChangeEvent);
|
|
||||||
return (
|
|
||||||
e.state == state &&
|
|
||||||
!e.isExtraState &&
|
|
||||||
isEnabled == e.isEnabled &&
|
|
||||||
id == getAccessibleDOMNodeID(e.accessible)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test aria-expanded on a button
|
// Test aria-expanded on a button
|
||||||
addAccessibleTask(
|
addAccessibleTask(
|
||||||
`hello world<br>
|
`hello world<br>
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
/* import-globals-from ../../mochitest/states.js */
|
||||||
|
loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test outline, outline rows with computed properties
|
* Test outline, outline rows with computed properties
|
||||||
*/
|
*/
|
||||||
|
@ -457,3 +460,107 @@ addAccessibleTask(
|
||||||
is(treeItems[1].getAttributeValue("AXDisclosing"), 1);
|
is(treeItems[1].getAttributeValue("AXDisclosing"), 1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test outline rows correctly expose checkable, checked/unchecked/mixed status
|
||||||
|
addAccessibleTask(
|
||||||
|
`
|
||||||
|
<div role="tree" id="tree">
|
||||||
|
<div role="treeitem" aria-checked="false" id="l1">
|
||||||
|
Leaf 1
|
||||||
|
</div>
|
||||||
|
<div role="treeitem" aria-checked="true" id="l2">
|
||||||
|
Leaf 2
|
||||||
|
</div>
|
||||||
|
<div role="treeitem" id="l3">
|
||||||
|
Leaf 3
|
||||||
|
</div>
|
||||||
|
<div role="treeitem" aria-checked="mixed" id="l4">
|
||||||
|
Leaf 4
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
const tree = getNativeInterface(accDoc, "tree");
|
||||||
|
const treeItems = tree.getAttributeValue("AXChildren");
|
||||||
|
|
||||||
|
is(treeItems.length, 4, "Outline has four direct children");
|
||||||
|
is(
|
||||||
|
treeItems[0].getAttributeValue("AXValue"),
|
||||||
|
0,
|
||||||
|
"Child one is not checked"
|
||||||
|
);
|
||||||
|
is(treeItems[1].getAttributeValue("AXValue"), 1, "Child two is checked");
|
||||||
|
is(
|
||||||
|
treeItems[2].getAttributeValue("AXValue"),
|
||||||
|
null,
|
||||||
|
"Child three is not checkable and has no val"
|
||||||
|
);
|
||||||
|
is(treeItems[3].getAttributeValue("AXValue"), 2, "Child four is mixed");
|
||||||
|
|
||||||
|
let stateChanged = Promise.all([
|
||||||
|
waitForMacEvent("AXValueChanged", "l1"),
|
||||||
|
waitForStateChange("l1", STATE_CHECKED, true),
|
||||||
|
]);
|
||||||
|
// We should get a state change event for checked.
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
content.document
|
||||||
|
.getElementById("l1")
|
||||||
|
.setAttribute("aria-checked", "true");
|
||||||
|
});
|
||||||
|
await stateChanged;
|
||||||
|
is(treeItems[0].getAttributeValue("AXValue"), 1, "Child one is checked");
|
||||||
|
|
||||||
|
stateChanged = Promise.all([
|
||||||
|
waitForMacEvent("AXValueChanged", "l2"),
|
||||||
|
waitForMacEvent("AXValueChanged", "l2"),
|
||||||
|
waitForStateChange("l2", STATE_CHECKED, false),
|
||||||
|
waitForStateChange("l2", STATE_CHECKABLE, false),
|
||||||
|
]);
|
||||||
|
// We should get a state change event for both checked and checkable,
|
||||||
|
// and value changes for both.
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
content.document.getElementById("l2").removeAttribute("aria-checked");
|
||||||
|
});
|
||||||
|
await stateChanged;
|
||||||
|
is(
|
||||||
|
treeItems[1].getAttributeValue("AXValue"),
|
||||||
|
null,
|
||||||
|
"Child two is not checkable and has no val"
|
||||||
|
);
|
||||||
|
|
||||||
|
stateChanged = Promise.all([
|
||||||
|
waitForMacEvent("AXValueChanged", "l3"),
|
||||||
|
waitForMacEvent("AXValueChanged", "l3"),
|
||||||
|
waitForStateChange("l3", STATE_CHECKED, true),
|
||||||
|
waitForStateChange("l3", STATE_CHECKABLE, true),
|
||||||
|
]);
|
||||||
|
// We should get a state change event for both checked and checkable,
|
||||||
|
// and value changes for each.
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
content.document
|
||||||
|
.getElementById("l3")
|
||||||
|
.setAttribute("aria-checked", "true");
|
||||||
|
});
|
||||||
|
await stateChanged;
|
||||||
|
is(treeItems[2].getAttributeValue("AXValue"), 1, "Child three is checked");
|
||||||
|
|
||||||
|
stateChanged = Promise.all([
|
||||||
|
waitForMacEvent("AXValueChanged", "l4"),
|
||||||
|
waitForMacEvent("AXValueChanged", "l4"),
|
||||||
|
waitForStateChange("l4", STATE_MIXED, false),
|
||||||
|
waitForStateChange("l4", STATE_CHECKABLE, false),
|
||||||
|
]);
|
||||||
|
// We should get a state change event for both mixed and checkable,
|
||||||
|
// and value changes for each.
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
content.document.getElementById("l4").removeAttribute("aria-checked");
|
||||||
|
});
|
||||||
|
await stateChanged;
|
||||||
|
is(
|
||||||
|
treeItems[3].getAttributeValue("AXValue"),
|
||||||
|
null,
|
||||||
|
"Child four is not checkable and has no value"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -127,3 +127,15 @@ function stringForRange(macDoc, range) {
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function waitForStateChange(id, state, isEnabled) {
|
||||||
|
return waitForEvent(EVENT_STATE_CHANGE, e => {
|
||||||
|
e.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||||
|
return (
|
||||||
|
e.state == state &&
|
||||||
|
!e.isExtraState &&
|
||||||
|
isEnabled == e.isEnabled &&
|
||||||
|
id == getAccessibleDOMNodeID(e.accessible)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче