зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1733263 - P5: Rely on DOMMenuItem events for ACTIVE state changes in select elements. r=Jamie
1. Use dom events in RootAccessible to fire ACTIVE state changes. 2. Add DOMMenuItemInactive events to nsListControlFrame and fire it on the previously active option. 3. Don't fire those DOM events on collapsed combo boxes. 4. Add ACTIVE state change events for collapsed combo box options. Differential Revision: https://phabricator.services.mozilla.com/D130298
This commit is contained in:
Родитель
c96fcc25b5
Коммит
379e516511
|
@ -399,6 +399,9 @@ void RootAccessible::ProcessDOMEvent(Event* aDOMEvent, nsINode* aTarget) {
|
|||
accessible);
|
||||
}
|
||||
} else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
|
||||
RefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(accessible, states::ACTIVE, true);
|
||||
nsEventShell::FireEvent(event);
|
||||
FocusMgr()->ActiveItemChanged(accessible);
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus)) {
|
||||
|
@ -406,6 +409,10 @@ void RootAccessible::ProcessDOMEvent(Event* aDOMEvent, nsINode* aTarget) {
|
|||
}
|
||||
#endif
|
||||
} else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
|
||||
RefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(accessible, states::ACTIVE, false);
|
||||
nsEventShell::FireEvent(event);
|
||||
|
||||
// Process DOMMenuItemInactive event for autocomplete only because this is
|
||||
// unique widget that may acquire focus from autocomplete popup while popup
|
||||
// stays open and has no active item. In case of XUL tree autocomplete
|
||||
|
|
|
@ -235,6 +235,22 @@ nsRect HTMLSelectOptionAccessible::RelativeBounds(
|
|||
return HyperTextAccessibleWrap::RelativeBounds(aBoundingFrame);
|
||||
}
|
||||
|
||||
nsresult HTMLSelectOptionAccessible::HandleAccEvent(AccEvent* aEvent) {
|
||||
nsresult rv = HyperTextAccessibleWrap::HandleAccEvent(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
AccStateChangeEvent* event = downcast_accEvent(aEvent);
|
||||
if (event && (event->GetState() == states::SELECTED)) {
|
||||
if (!ContainerWidget()->AreItemsOperable()) {
|
||||
// Collapsed options' ACTIVE state reflects their SELECT state.
|
||||
nsEventShell::FireEvent(this, states::ACTIVE, event->IsStateEnabled(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void HTMLSelectOptionAccessible::ActionNameAt(uint8_t aIndex,
|
||||
nsAString& aName) {
|
||||
if (aIndex == eAction_Select) aName.AssignLiteral("select");
|
||||
|
|
|
@ -71,6 +71,8 @@ class HTMLSelectOptionAccessible : public HyperTextAccessibleWrap {
|
|||
virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
|
||||
virtual void SetSelected(bool aSelect) override;
|
||||
|
||||
nsresult HandleAccEvent(AccEvent* aEvent) override;
|
||||
|
||||
// ActionAccessible
|
||||
virtual uint8_t ActionCount() const override;
|
||||
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
<script type="application/javascript">
|
||||
// gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
// gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
async function doTests() {
|
||||
|
@ -39,20 +38,33 @@
|
|||
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_SELECTION, "orange"]],
|
||||
unexpected: [[EVENT_FOCUS]]
|
||||
unexpected: [
|
||||
[EVENT_FOCUS],
|
||||
stateChangeEventArgs("orange", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
});
|
||||
// item is selected and stays focused
|
||||
// item is selected and stays focused and active
|
||||
synthesizeKey("VK_DOWN");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(EVENT_FOCUS, "apple");
|
||||
p = waitForEvents([
|
||||
stateChangeEventArgs("orange", EXT_STATE_ACTIVE, false, true),
|
||||
stateChangeEventArgs("apple", EXT_STATE_ACTIVE, true, true),
|
||||
[EVENT_FOCUS, "apple"],
|
||||
]);
|
||||
// last selected item is focused
|
||||
synthesizeKey("VK_DOWN", { shiftKey: true });
|
||||
await p;
|
||||
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_FOCUS, "orange"]],
|
||||
unexpected: [[EVENT_FOCUS, "apple"]]
|
||||
expected: [
|
||||
[EVENT_FOCUS, "orange"],
|
||||
stateChangeEventArgs("orange", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
unexpected: [
|
||||
[EVENT_FOCUS, "apple"],
|
||||
stateChangeEventArgs("apple", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
});
|
||||
// no focus event if nothing is changed
|
||||
synthesizeKey("VK_DOWN");
|
||||
|
@ -65,7 +77,10 @@
|
|||
synthesizeKey("VK_TAB");
|
||||
await p;
|
||||
|
||||
p = waitForEvent(EVENT_FOCUS, "orange");
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_FOCUS, "orange"]],
|
||||
unexpected: [stateChangeEventArgs("orange", EXT_STATE_ACTIVE, true, true)],
|
||||
});
|
||||
// current item is focused
|
||||
synthesizeKey("VK_TAB", { shiftKey: true });
|
||||
await p;
|
||||
|
@ -75,8 +90,11 @@
|
|||
await p;
|
||||
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_SELECTION, "cb_apple"]],
|
||||
unexpected: [[EVENT_FOCUS]]
|
||||
expected: [
|
||||
[EVENT_SELECTION, "cb_apple"],
|
||||
stateChangeEventArgs("cb_apple", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
unexpected: [[EVENT_FOCUS]],
|
||||
});
|
||||
// collapsed combobox keeps a focus
|
||||
synthesizeKey("VK_DOWN");
|
||||
|
@ -87,7 +105,12 @@
|
|||
synthesizeKey("VK_DOWN", { altKey: true });
|
||||
await p;
|
||||
|
||||
p = waitForEvent(EVENT_FOCUS, "cb_orange");
|
||||
p = waitForEvents({
|
||||
expected: [
|
||||
[EVENT_SELECTION, "cb_orange"],
|
||||
stateChangeEventArgs("cb_orange", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
});
|
||||
// selected item is focused for expanded combobox
|
||||
synthesizeKey("VK_UP");
|
||||
await p;
|
||||
|
@ -106,15 +129,25 @@
|
|||
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_SELECTION, "orange"]],
|
||||
unexpected: [[EVENT_FOCUS]]
|
||||
unexpected: [
|
||||
[EVENT_FOCUS],
|
||||
stateChangeEventArgs("orange", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
});
|
||||
// An unfocused selectable list gets selection change events,
|
||||
// but not active or focus change events.
|
||||
getNode("list").selectedIndex = getNode("orange").index;
|
||||
await p;
|
||||
|
||||
p = waitForEvents({
|
||||
expected: [[EVENT_SELECTION, "cb_apple"]],
|
||||
unexpected: [[EVENT_FOCUS]]
|
||||
expected: [
|
||||
[EVENT_SELECTION, "cb_apple"],
|
||||
stateChangeEventArgs("cb_apple", EXT_STATE_ACTIVE, true, true),
|
||||
],
|
||||
unexpected: [[EVENT_FOCUS]],
|
||||
});
|
||||
// An unfocused selectable combobox gets selection change events,
|
||||
// and active state change events, but not focus.
|
||||
getNode("cb_apple").selected = true;
|
||||
await p;
|
||||
|
||||
|
|
|
@ -620,6 +620,9 @@ bool nsListControlFrame::SingleSelection(int32_t aClickedIndex,
|
|||
mComboboxFrame->UpdateRecentIndex(GetSelectedIndex());
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
nsCOMPtr<nsIContent> prevOption = GetCurrentOption();
|
||||
#endif
|
||||
bool wasChanged = false;
|
||||
// Get Current selection
|
||||
if (aDoToggle) {
|
||||
|
@ -634,17 +637,12 @@ bool nsListControlFrame::SingleSelection(int32_t aClickedIndex,
|
|||
return wasChanged;
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
|
||||
#endif
|
||||
mStartSelectionIndex = aClickedIndex;
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
InvalidateFocus();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (isCurrentOptionChanged) {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
FireMenuItemActiveEvent(prevOption);
|
||||
#endif
|
||||
|
||||
return wasChanged;
|
||||
|
@ -766,15 +764,13 @@ bool nsListControlFrame::PerformSelection(int32_t aClickedIndex, bool aIsShift,
|
|||
mStartSelectionIndex = aClickedIndex;
|
||||
}
|
||||
#ifdef ACCESSIBILITY
|
||||
bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
|
||||
nsCOMPtr<nsIContent> prevOption = GetCurrentOption();
|
||||
#endif
|
||||
mEndSelectionIndex = aClickedIndex;
|
||||
InvalidateFocus();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (isCurrentOptionChanged) {
|
||||
FireMenuItemActiveEvent();
|
||||
}
|
||||
FireMenuItemActiveEvent(prevOption);
|
||||
#endif
|
||||
} else if (aIsControl) {
|
||||
wasChanged = SingleSelection(aClickedIndex, true); // might destroy us
|
||||
|
@ -1289,6 +1285,10 @@ nsListControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) {
|
|||
mComboboxFrame->UpdateRecentIndex(NS_SKIP_NOTIFY_INDEX);
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
nsCOMPtr<nsIContent> prevOption = GetCurrentOption();
|
||||
#endif
|
||||
|
||||
AutoWeakFrame weakFrame(this);
|
||||
ScrollToIndex(aNewIndex);
|
||||
if (!weakFrame.IsAlive()) {
|
||||
|
@ -1299,7 +1299,9 @@ nsListControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) {
|
|||
InvalidateFocus();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
FireMenuItemActiveEvent();
|
||||
if (aOldIndex != aNewIndex) {
|
||||
FireMenuItemActiveEvent(prevOption);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1355,7 +1357,7 @@ void nsListControlFrame::AboutToDropDown() {
|
|||
return;
|
||||
}
|
||||
#ifdef ACCESSIBILITY
|
||||
FireMenuItemActiveEvent(); // Inform assistive tech what got focus
|
||||
FireMenuItemActiveEvent(nullptr); // Inform assistive tech what got focus
|
||||
#endif
|
||||
}
|
||||
mItemSelectionStarted = false;
|
||||
|
@ -1579,17 +1581,25 @@ bool nsListControlFrame::IgnoreMouseEventForSelection(dom::Event* aEvent) {
|
|||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
void nsListControlFrame::FireMenuItemActiveEvent() {
|
||||
if (mFocused != this && !IsInDropDownMode()) {
|
||||
void nsListControlFrame::FireMenuItemActiveEvent(nsIContent* aPreviousOption) {
|
||||
if ((mFocused != this && !IsInDropDownMode()) ||
|
||||
(IsInDropDownMode() && !mComboboxFrame->IsDroppedDown())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> optionContent = GetCurrentOption();
|
||||
if (!optionContent) {
|
||||
nsIContent* optionContent = GetCurrentOption();
|
||||
if (aPreviousOption == optionContent) {
|
||||
// No change
|
||||
return;
|
||||
}
|
||||
|
||||
FireDOMEvent(u"DOMMenuItemActive"_ns, optionContent);
|
||||
if (aPreviousOption) {
|
||||
FireDOMEvent(u"DOMMenuItemInactive"_ns, aPreviousOption);
|
||||
}
|
||||
|
||||
if (optionContent) {
|
||||
FireDOMEvent(u"DOMMenuItemActive"_ns, optionContent);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2311,6 +2321,9 @@ void nsListControlFrame::PostHandleKeyEvent(int32_t aNewIndex,
|
|||
AutoWeakFrame weakFrame(this);
|
||||
bool wasChanged = false;
|
||||
if (aIsControlOrMeta && !aIsShift && aCharCode != ' ') {
|
||||
#ifdef ACCESSIBILITY
|
||||
nsCOMPtr<nsIContent> prevOption = GetCurrentOption();
|
||||
#endif
|
||||
mStartSelectionIndex = aNewIndex;
|
||||
mEndSelectionIndex = aNewIndex;
|
||||
InvalidateFocus();
|
||||
|
@ -2320,7 +2333,7 @@ void nsListControlFrame::PostHandleKeyEvent(int32_t aNewIndex,
|
|||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
FireMenuItemActiveEvent();
|
||||
FireMenuItemActiveEvent(prevOption);
|
||||
#endif
|
||||
} else if (mControlSelectMode && aCharCode == ' ') {
|
||||
wasChanged = SingleSelection(aNewIndex, true);
|
||||
|
|
|
@ -249,7 +249,8 @@ class nsListControlFrame final : public nsHTMLScrollFrame,
|
|||
* fire a native focus event for accessibility
|
||||
* (Some 3rd party products need to track our focus)
|
||||
*/
|
||||
void FireMenuItemActiveEvent(); // Inform assistive tech what got focused
|
||||
void FireMenuItemActiveEvent(
|
||||
nsIContent* aPreviousOption); // Inform assistive tech what got focused
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
|
Загрузка…
Ссылка в новой задаче