зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1722396 - P3: Compare old state bits with new ones to determine state change events. r=morgan
Needed to tweak tests a bit because the code is now more descerning wheter to fire an event or not. Will do so only if the state actually changes. Depends on D120901 Differential Revision: https://phabricator.services.mozilla.com/D120902
This commit is contained in:
Родитель
1924362000
Коммит
da0fa820e2
|
@ -688,11 +688,10 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
|
|||
RelocateARIAOwnedIfNeeded(aElement);
|
||||
}
|
||||
|
||||
// For these attributes we store the state before the attribute changes so we
|
||||
// can determine if the attribute change changes the state.
|
||||
if (aAttribute == nsGkAtoms::aria_disabled || aAttribute == nsGkAtoms::href ||
|
||||
aAttribute == nsGkAtoms::disabled || aAttribute == nsGkAtoms::tabindex ||
|
||||
aAttribute == nsGkAtoms::contenteditable) {
|
||||
// If attribute affects accessible's state, store the old state so we can
|
||||
// later compare it against the state of the accessible after the attribute
|
||||
// change.
|
||||
if (accessible->AttributeChangesState(aAttribute)) {
|
||||
mPrevStateBits = accessible->State();
|
||||
} else {
|
||||
mPrevStateBits = 0;
|
||||
|
|
|
@ -1133,6 +1133,24 @@ already_AddRefed<AccAttributes> LocalAccessible::NativeAttributes() {
|
|||
return attributes.forget();
|
||||
}
|
||||
|
||||
bool LocalAccessible::AttributeChangesState(nsAtom* aAttribute) {
|
||||
return aAttribute == nsGkAtoms::aria_disabled ||
|
||||
aAttribute == nsGkAtoms::disabled ||
|
||||
aAttribute == nsGkAtoms::tabindex ||
|
||||
aAttribute == nsGkAtoms::aria_required ||
|
||||
aAttribute == nsGkAtoms::aria_invalid ||
|
||||
aAttribute == nsGkAtoms::aria_expanded ||
|
||||
aAttribute == nsGkAtoms::aria_checked ||
|
||||
(aAttribute == nsGkAtoms::aria_pressed && IsButton()) ||
|
||||
aAttribute == nsGkAtoms::aria_readonly ||
|
||||
aAttribute == nsGkAtoms::aria_current ||
|
||||
aAttribute == nsGkAtoms::aria_haspopup ||
|
||||
aAttribute == nsGkAtoms::aria_busy ||
|
||||
aAttribute == nsGkAtoms::aria_multiline ||
|
||||
aAttribute == nsGkAtoms::contenteditable ||
|
||||
(aAttribute == nsGkAtoms::href && IsHTMLLink());
|
||||
}
|
||||
|
||||
void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
||||
nsAtom* aAttribute, int32_t aModType,
|
||||
const nsAttrValue* aOldValue,
|
||||
|
@ -1153,41 +1171,18 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
|||
// XXX todo: invalidate accessible when aria state changes affect exposed
|
||||
// role filed as bug 472143
|
||||
|
||||
// Universal boolean properties that don't require a role. Fire the state
|
||||
// change when disabled or aria-disabled attribute is set.
|
||||
// Note. Checking the XUL or HTML namespace would not seem to gain us
|
||||
// anything, because disabled attribute really is going to mean the same
|
||||
// thing in any namespace.
|
||||
// Note. We use the attribute instead of the disabled state bit because
|
||||
// ARIA's aria-disabled does not affect the disabled state bit.
|
||||
if (aAttribute == nsGkAtoms::disabled ||
|
||||
aAttribute == nsGkAtoms::aria_disabled) {
|
||||
// disabled can affect focusable state
|
||||
MaybeFireFocusableStateChange((aOldState & states::FOCUSABLE) != 0);
|
||||
|
||||
// Do nothing if state wasn't changed (like @aria-disabled was removed but
|
||||
// @disabled is still presented).
|
||||
uint64_t unavailableState = (State() & states::UNAVAILABLE);
|
||||
if ((aOldState & states::UNAVAILABLE) == unavailableState) {
|
||||
return;
|
||||
if (AttributeChangesState(aAttribute)) {
|
||||
uint64_t currState = State();
|
||||
uint64_t diffState = currState ^ aOldState;
|
||||
if (diffState) {
|
||||
for (uint64_t state = 1; state <= states::LAST_ENTRY; state <<= 1) {
|
||||
if (diffState & state) {
|
||||
RefPtr<AccEvent> stateChangeEvent =
|
||||
new AccStateChangeEvent(this, state, (currState & state));
|
||||
mDoc->FireDelayedEvent(stateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<AccEvent> enabledChangeEvent =
|
||||
new AccStateChangeEvent(this, states::ENABLED, !unavailableState);
|
||||
mDoc->FireDelayedEvent(enabledChangeEvent);
|
||||
|
||||
RefPtr<AccEvent> sensitiveChangeEvent =
|
||||
new AccStateChangeEvent(this, states::SENSITIVE, !unavailableState);
|
||||
mDoc->FireDelayedEvent(sensitiveChangeEvent);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::tabindex) {
|
||||
// Fire a focusable state change event if the previous state was different.
|
||||
// It may be the same if tabindex is on a redundantly focusable element.
|
||||
MaybeFireFocusableStateChange((aOldState & states::FOCUSABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
// When a details object has its open attribute changed
|
||||
|
@ -1206,61 +1201,24 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
|||
}
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_required) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::REQUIRED);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_invalid) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::INVALID);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// We treat aria-expanded as a global ARIA state for historical reasons
|
||||
if (aAttribute == nsGkAtoms::aria_expanded) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::EXPANDED);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// For aria attributes like drag and drop changes we fire a generic attribute
|
||||
// change event; at least until native API comes up with a more meaningful
|
||||
// event.
|
||||
uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
|
||||
if (!(attrFlags & ATTR_BYPASSOBJ)) {
|
||||
RefPtr<AccEvent> event = new AccObjectAttrChangedEvent(this, aAttribute);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
// Check for namespaced ARIA attribute
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
// Check for hyphenated aria-foo property?
|
||||
if (StringBeginsWith(nsDependentAtomString(aAttribute), u"aria-"_ns)) {
|
||||
uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
|
||||
if (!(attrFlags & ATTR_BYPASSOBJ)) {
|
||||
// For aria attributes like drag and drop changes we fire a generic
|
||||
// attribute change event; at least until native API comes up with a
|
||||
// more meaningful event.
|
||||
RefPtr<AccEvent> event =
|
||||
new AccObjectAttrChangedEvent(this, aAttribute);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dom::Element* elm = Elm();
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_checked ||
|
||||
(IsButton() && aAttribute == nsGkAtoms::aria_pressed)) {
|
||||
const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked)
|
||||
? states::CHECKED
|
||||
: states::PRESSED;
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, kState);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
|
||||
bool wasMixed = aOldValue->Equals(nsGkAtoms::mixed, eCaseMatters);
|
||||
bool isMixed = elm->AttrValueIs(kNameSpaceID_None, aAttribute,
|
||||
nsGkAtoms::mixed, eCaseMatters);
|
||||
if (isMixed != wasMixed) {
|
||||
RefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(this, states::MIXED, isMixed);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_readonly) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::READONLY);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire text value change event whenever aria-valuetext is changed.
|
||||
if (aAttribute == nsGkAtoms::aria_valuetext) {
|
||||
mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE, this);
|
||||
|
@ -1277,18 +1235,6 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
|||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_current) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::CURRENT);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_haspopup) {
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::HASPOPUP);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_owns) {
|
||||
mDoc->Controller()->ScheduleRelocation(this);
|
||||
}
|
||||
|
@ -1380,23 +1326,6 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
|||
}
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_busy) {
|
||||
bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
|
||||
eCaseMatters);
|
||||
RefPtr<AccEvent> event = new AccStateChangeEvent(this, states::BUSY, isOn);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_multiline) {
|
||||
bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
|
||||
eCaseMatters);
|
||||
RefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(this, states::MULTI_LINE, isOn);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// ARIA or XUL selection
|
||||
if ((mContent->IsXULElement() && aAttribute == nsGkAtoms::selected) ||
|
||||
aAttribute == nsGkAtoms::aria_selected) {
|
||||
|
@ -1416,37 +1345,12 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
|
|||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::contenteditable) {
|
||||
RefPtr<AccEvent> editableChangeEvent =
|
||||
new AccStateChangeEvent(this, states::EDITABLE);
|
||||
mDoc->FireDelayedEvent(editableChangeEvent);
|
||||
// Fire a focusable state change event if the previous state was different.
|
||||
// It may be the same if contenteditable is set on a node that doesn't
|
||||
// support it. Like an <input>.
|
||||
MaybeFireFocusableStateChange((aOldState & states::FOCUSABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::value) {
|
||||
if (IsProgress()) {
|
||||
mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aModType == dom::MutationEvent_Binding::REMOVAL ||
|
||||
aModType == dom::MutationEvent_Binding::ADDITION) {
|
||||
if (aAttribute == nsGkAtoms::href) {
|
||||
if (IsHTMLLink() && !nsCoreUtils::HasClickListener(mContent)) {
|
||||
RefPtr<AccEvent> linkedChangeEvent =
|
||||
new AccStateChangeEvent(this, states::LINKED);
|
||||
mDoc->FireDelayedEvent(linkedChangeEvent);
|
||||
// Fire a focusable state change event if the previous state was
|
||||
// different. It may be the same if there is tabindex on this link.
|
||||
MaybeFireFocusableStateChange((aOldState & states::FOCUSABLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupPos LocalAccessible::GroupPosition() {
|
||||
|
|
|
@ -931,6 +931,13 @@ class LocalAccessible : public nsISupports, public Accessible {
|
|||
*/
|
||||
virtual already_AddRefed<AccAttributes> NativeAttributes();
|
||||
|
||||
/**
|
||||
* The given attribute has the potential of changing the accessible's state.
|
||||
* This is used to capture the state before the attribute change and compare
|
||||
* it with the state after.
|
||||
*/
|
||||
bool AttributeChangesState(nsAtom* aAttribute);
|
||||
|
||||
/**
|
||||
* Notify accessible that a DOM attribute on its associated content has
|
||||
* changed. This allows the accessible to update its state and emit any
|
||||
|
|
|
@ -31,13 +31,11 @@ addAccessibleTask(
|
|||
"Correct aria-current for #two"
|
||||
);
|
||||
|
||||
let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "one");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("one")
|
||||
.setAttribute("aria-current", "step");
|
||||
});
|
||||
await stateChanged;
|
||||
|
||||
is(
|
||||
one.getAttributeValue("AXARIACurrent"),
|
||||
|
@ -45,7 +43,7 @@ addAccessibleTask(
|
|||
"Correct aria-current for #one"
|
||||
);
|
||||
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "one");
|
||||
let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "one");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.getElementById("one").removeAttribute("aria-current");
|
||||
});
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from ../../mochitest/states.js */
|
||||
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
|
||||
addAccessibleTask(
|
||||
`hello world<br>
|
||||
|
@ -13,7 +28,10 @@ addAccessibleTask(
|
|||
let button = getNativeInterface(accDoc, "b");
|
||||
is(button.getAttributeValue("AXExpanded"), 0, "button is not expanded");
|
||||
|
||||
let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "b");
|
||||
let stateChanged = Promise.all([
|
||||
waitForStateChange("b", STATE_EXPANDED, true),
|
||||
waitForStateChange("b", STATE_COLLAPSED, false),
|
||||
]);
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("b")
|
||||
|
@ -22,7 +40,7 @@ addAccessibleTask(
|
|||
await stateChanged;
|
||||
is(button.getAttributeValue("AXExpanded"), 1, "button is expanded");
|
||||
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "b");
|
||||
stateChanged = waitForStateChange("b", STATE_EXPANDED, false);
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.getElementById("b").removeAttribute("aria-expanded");
|
||||
});
|
||||
|
|
|
@ -91,13 +91,11 @@ addAccessibleTask(
|
|||
"Correct AXHasPopup val for button with menu"
|
||||
);
|
||||
|
||||
attrChanged = waitForEvent(EVENT_STATE_CHANGE, "menu");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("menu")
|
||||
.setAttribute("aria-haspopup", "true");
|
||||
});
|
||||
await attrChanged;
|
||||
|
||||
is(
|
||||
menuID.getAttributeValue("AXPopupValue"),
|
||||
|
@ -140,13 +138,11 @@ addAccessibleTask(
|
|||
"Correct AXHasPopup for button with listbox"
|
||||
);
|
||||
|
||||
attrChanged = waitForEvent(EVENT_STATE_CHANGE, "listbox");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("listbox")
|
||||
.setAttribute("aria-haspopup", "true");
|
||||
});
|
||||
await attrChanged;
|
||||
|
||||
is(
|
||||
listboxID.getAttributeValue("AXPopupValue"),
|
||||
|
@ -191,13 +187,11 @@ addAccessibleTask(
|
|||
"Correct AXHasPopup for button with tree"
|
||||
);
|
||||
|
||||
attrChanged = waitForEvent(EVENT_STATE_CHANGE, "tree");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("tree")
|
||||
.setAttribute("aria-haspopup", "true");
|
||||
});
|
||||
await attrChanged;
|
||||
|
||||
is(
|
||||
treeID.getAttributeValue("AXPopupValue"),
|
||||
|
@ -240,13 +234,11 @@ addAccessibleTask(
|
|||
"Correct AXHasPopup for button with grid"
|
||||
);
|
||||
|
||||
attrChanged = waitForEvent(EVENT_STATE_CHANGE, "grid");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("grid")
|
||||
.setAttribute("aria-haspopup", "true");
|
||||
});
|
||||
await attrChanged;
|
||||
|
||||
is(
|
||||
gridID.getAttributeValue("AXPopupValue"),
|
||||
|
@ -289,13 +281,11 @@ addAccessibleTask(
|
|||
"Correct AXHasPopup for button with dialog"
|
||||
);
|
||||
|
||||
attrChanged = waitForEvent(EVENT_STATE_CHANGE, "dialog");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("dialog")
|
||||
.setAttribute("aria-haspopup", "true");
|
||||
});
|
||||
await attrChanged;
|
||||
|
||||
is(
|
||||
dialogID.getAttributeValue("AXPopupValue"),
|
||||
|
|
|
@ -40,6 +40,18 @@ addAccessibleTask(
|
|||
}
|
||||
);
|
||||
|
||||
function waitForLinkedChange(id, isEnabled) {
|
||||
return waitForEvent(EVENT_STATE_CHANGE, e => {
|
||||
e.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||
return (
|
||||
e.state == STATE_LINKED &&
|
||||
!e.isExtraState &&
|
||||
isEnabled == e.isEnabled &&
|
||||
id == getAccessibleDOMNodeID(e.accessible)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test linked vs unlinked anchor tags
|
||||
*/
|
||||
|
@ -81,7 +93,7 @@ addAccessibleTask(
|
|||
"bare <a> gets correct group role"
|
||||
);
|
||||
|
||||
let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "link1");
|
||||
let stateChanged = waitForLinkedChange("link1", false);
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.getElementById("link1").removeAttribute("href");
|
||||
});
|
||||
|
@ -92,7 +104,7 @@ addAccessibleTask(
|
|||
"<a> stripped from href gets group role"
|
||||
);
|
||||
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "link2");
|
||||
stateChanged = waitForLinkedChange("link2", false);
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.getElementById("link2").removeAttribute("onclick");
|
||||
});
|
||||
|
@ -103,7 +115,7 @@ addAccessibleTask(
|
|||
"<a> stripped from onclick gets group role"
|
||||
);
|
||||
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "link3");
|
||||
stateChanged = waitForLinkedChange("link3", true);
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("link3")
|
||||
|
|
|
@ -72,6 +72,21 @@ addAccessibleTask(
|
|||
"Correct required after false set for ariaCheckbox"
|
||||
);
|
||||
|
||||
// Change aria-required, verify AXRequired is updated
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaCheckbox");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("ariaCheckbox")
|
||||
.setAttribute("aria-required", "true");
|
||||
});
|
||||
await stateChanged;
|
||||
|
||||
is(
|
||||
ariaCheckbox.getAttributeValue("AXRequired"),
|
||||
1,
|
||||
"Correct required after true set for ariaCheckbox"
|
||||
);
|
||||
|
||||
// Remove aria-required, verify AXRequired is updated
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaCheckbox");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
|
@ -102,6 +117,21 @@ addAccessibleTask(
|
|||
"Correct required after false set for ariaRadio"
|
||||
);
|
||||
|
||||
// Change aria-required, verify AXRequired is updated
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaRadio");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document
|
||||
.getElementById("ariaRadio")
|
||||
.setAttribute("aria-required", "true");
|
||||
});
|
||||
await stateChanged;
|
||||
|
||||
is(
|
||||
ariaRadio.getAttributeValue("AXRequired"),
|
||||
1,
|
||||
"Correct required after true set for ariaRadio"
|
||||
);
|
||||
|
||||
// Remove aria-required, verify AXRequired is updated
|
||||
stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaRadio");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
let PromEvents = {};
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/a11y/accessible/tests/mochitest/promisified-events.js",
|
||||
PromEvents);
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
|
@ -77,64 +80,72 @@
|
|||
};
|
||||
}
|
||||
|
||||
function setAttrOfMixedType(aID, aAttr, aState, aValue) {
|
||||
this.DOMNode = getNode(aID);
|
||||
function waitForStateChange(aID, aState, aIsEnabled) {
|
||||
return PromEvents.waitForEvent(EVENT_STATE_CHANGE, e => {
|
||||
e.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||
return e.state == aState && !e.isExtraState &&
|
||||
aIsEnabled == e.isEnabled && getAccessible(aID) == e.accessible;
|
||||
});
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new stateChangeChecker(aState, kOrdinalState,
|
||||
aValue == "true", this.DOMNode),
|
||||
];
|
||||
async function testToggleAttribute(aID, aAttribute, aIncludeMixed) {
|
||||
let toggleState = aAttribute == "aria-pressed" ? STATE_PRESSED : STATE_CHECKED;
|
||||
|
||||
if (hasState(aID, STATE_MIXED) || aValue == "mixed") {
|
||||
this.eventSeq.push(
|
||||
new stateChangeChecker(STATE_MIXED, kOrdinalState,
|
||||
aValue == "mixed", this.DOMNode)
|
||||
);
|
||||
// bug 472142. Role changes here if aria-pressed is added,
|
||||
// accessible should be recreated?
|
||||
let stateChange = waitForStateChange(aID, toggleState, true);
|
||||
getNode(aID).setAttribute(aAttribute, "true");
|
||||
await stateChange;
|
||||
|
||||
stateChange = waitForStateChange(aID, toggleState, false);
|
||||
getNode(aID).setAttribute(aAttribute, "false");
|
||||
await stateChange;
|
||||
|
||||
if (aIncludeMixed) {
|
||||
stateChange = waitForStateChange(aID, STATE_MIXED, true);
|
||||
getNode(aID).setAttribute(aAttribute, "mixed");
|
||||
await stateChange;
|
||||
|
||||
stateChange = waitForStateChange(aID, STATE_MIXED, false);
|
||||
getNode(aID).setAttribute(aAttribute, "");
|
||||
await stateChange;
|
||||
}
|
||||
|
||||
this.invoke = function setAttrOfMixedType_invoke() {
|
||||
this.DOMNode.setAttribute(aAttr, aValue);
|
||||
};
|
||||
stateChange = waitForStateChange(aID, toggleState, true);
|
||||
getNode(aID).setAttribute(aAttribute, "true");
|
||||
await stateChange;
|
||||
|
||||
this.getID = function setAttrOfMixedType_getID() {
|
||||
return prettyName(aID) + " " + aAttr + " changed to '" + aValue + "'";
|
||||
};
|
||||
}
|
||||
if (aIncludeMixed) {
|
||||
stateChange = Promise.all([
|
||||
waitForStateChange(aID, STATE_MIXED, true),
|
||||
waitForStateChange(aID, toggleState, false)]);
|
||||
getNode(aID).setAttribute(aAttribute, "mixed");
|
||||
await stateChange;
|
||||
|
||||
function setPressed(aID, aValue) {
|
||||
this.__proto__ =
|
||||
new setAttrOfMixedType(aID, "aria-pressed", STATE_PRESSED, aValue);
|
||||
}
|
||||
|
||||
function setChecked(aID, aValue) {
|
||||
this.__proto__ =
|
||||
new setAttrOfMixedType(aID, "aria-checked", STATE_CHECKED, aValue);
|
||||
}
|
||||
|
||||
function buildQueueForAttr(aList, aQueue, aID, aInvokerFunc) {
|
||||
for (var i = 0; i < aList.length; i++) {
|
||||
for (var j = i + 1; j < aList.length; j++) {
|
||||
// XXX: changes from/to "undefined"/"" shouldn't fire state change
|
||||
// events, bug 472142.
|
||||
aQueue.push(new aInvokerFunc(aID, aList[i]));
|
||||
aQueue.push(new aInvokerFunc(aID, aList[j]));
|
||||
}
|
||||
stateChange = Promise.all([
|
||||
waitForStateChange(aID, STATE_MIXED, false),
|
||||
waitForStateChange(aID, toggleState, true)]);
|
||||
getNode(aID).setAttribute(aAttribute, "true");
|
||||
await stateChange;
|
||||
}
|
||||
|
||||
// bug 472142. Role changes here too if aria-pressed is removed,
|
||||
// accessible should be recreated?
|
||||
stateChange = waitForStateChange(aID, toggleState, false);
|
||||
getNode(aID).removeAttribute(aAttribute);
|
||||
await stateChange;
|
||||
}
|
||||
|
||||
function buildQueueForAttrOfMixedType(aQueue, aID, aInvokerFunc) {
|
||||
var list = [ "", "undefined", "false", "true", "mixed" ];
|
||||
buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
|
||||
}
|
||||
|
||||
function buildQueueForAttrOfBoolType(aQueue, aID, aInvokerFunc) {
|
||||
var list = [ "", "undefined", "false", "true" ];
|
||||
buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
|
||||
}
|
||||
|
||||
function doTests() {
|
||||
async function doTests() {
|
||||
gQueue = new eventQueue();
|
||||
|
||||
let queueFinished = new Promise(resolve => {
|
||||
gQueue.onFinish = function() {
|
||||
resolve();
|
||||
return DO_NOT_FINISH_TEST;
|
||||
};
|
||||
});
|
||||
|
||||
gQueue.push(new expandNode("section", true));
|
||||
gQueue.push(new expandNode("section", false));
|
||||
gQueue.push(new expandNode("div", true));
|
||||
|
@ -143,18 +154,22 @@
|
|||
gQueue.push(new busyify("aria_doc", true));
|
||||
gQueue.push(new busyify("aria_doc", false));
|
||||
|
||||
buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
|
||||
buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
|
||||
buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
|
||||
buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
|
||||
|
||||
gQueue.push(new makeCurrent("current_page_1", false, "false"));
|
||||
gQueue.push(new makeCurrent("current_page_2", true, "page"));
|
||||
gQueue.push(new makeCurrent("current_page_2", false, "false"));
|
||||
gQueue.push(new makeCurrent("current_page_3", true, "true"));
|
||||
gQueue.push(new makeCurrent("current_page_3", false, ""));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
gQueue.invoke();
|
||||
await queueFinished;
|
||||
// Tests beyond this point use await rather than eventQueue.
|
||||
|
||||
await testToggleAttribute("pressable", "aria-pressed", true);
|
||||
await testToggleAttribute("pressable_native", "aria-pressed", true);
|
||||
await testToggleAttribute("checkable", "aria-checked", true);
|
||||
await testToggleAttribute("checkableBool", "aria-checked", false);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
|
|
@ -159,28 +159,12 @@
|
|||
getNode("popupButton").setAttribute("aria-haspopup", "true");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, true, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "tree");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, true, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "menu");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, true, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "listbox");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, true, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "grid");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, false, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "false");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, true, "popupButton"));
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "dialog");
|
||||
getNode("popupButton").setAttribute("aria-haspopup", "true");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(...stateChange(STATE_HASPOPUP, false, false, "popupButton"));
|
||||
|
|
Загрузка…
Ссылка в новой задаче