mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
fix: [a11y] fire AXMenuOpened event when ARIA menu is added to DOM (#50506)
* fix: fire AXMenuOpened event when a visible ARIA menu instance is added to the DOM Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * fix: remove redundent FireMenuPopupEndForDeletedMenus MENU_POPUP_END for deleted menus is already handled by AXTreeManager::OnNodeWillBeDeleted, which fires the event directly on the menu node before destruction. Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * chore: add feature flag (kDynamicMenuPopupEvents) Co-authored-by: Keeley Hammond <vertedinde@electronjs.org> * chore: update patches Co-authored-by: Keeley Hammond <vertedinde@electronjs.org> * chore: update patches after trop --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond <khammond@slack-corp.com> Co-authored-by: Keeley Hammond <vertedinde@electronjs.org> Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
This commit is contained in:
@@ -147,3 +147,4 @@ fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
|
||||
fix_set_correct_app_id_on_linux.patch
|
||||
fix_pass_trigger_for_global_shortcuts_on_wayland.patch
|
||||
feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Keeley Hammond <khammond@slack-corp.com>
|
||||
Date: Thu, 19 Mar 2026 00:34:37 -0700
|
||||
Subject: fix: fire MENU_POPUP_START for dynamically created ARIA menus
|
||||
|
||||
When an ARIA menu element is dynamically created (e.g. via appendChild)
|
||||
rather than being shown by toggling visibility, the AXMenuOpened event
|
||||
was not fired. The OnIgnoredChanged path handles the visibility toggle
|
||||
case, but OnAtomicUpdateFinished did not fire MENU_POPUP_START for
|
||||
newly created menu nodes.
|
||||
|
||||
Previous attempts to fix this (crbug.com/1254875) were reverted because
|
||||
they fired the event too eagerly in OnNodeCreated (before the tree was
|
||||
fully formed) and without filtering, causing regressions with screen
|
||||
readers on pages that misused role="menu".
|
||||
|
||||
This fix addresses both issues:
|
||||
1. Fires MENU_POPUP_START in OnAtomicUpdateFinished (after the tree
|
||||
update is complete) rather than in OnNodeCreated.
|
||||
2. Only fires if the menu has at least one menuitem child, filtering
|
||||
out false positives from misused role="menu" elements.
|
||||
|
||||
MENU_POPUP_END for deleted menus is already handled by
|
||||
AXTreeManager::OnNodeWillBeDeleted, which fires the event directly
|
||||
on the menu node before destruction.
|
||||
|
||||
The change is behind the DynamicMenuPopupEvents feature flag, disabled
|
||||
by default, to allow stabilization before enabling by default. Enable
|
||||
with --enable-features=DynamicMenuPopupEvents.
|
||||
|
||||
This patch can be removed when a CL containing the fix is accepted
|
||||
into Chromium.
|
||||
|
||||
Bug: 40794596
|
||||
|
||||
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
|
||||
index 5e0d7a48b4a039db67b5cc6b7e86103739702b40..517fb5e9904f3907de177e172c76328910bb7333 100644
|
||||
--- a/ui/accessibility/ax_event_generator.cc
|
||||
+++ b/ui/accessibility/ax_event_generator.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "ui/accessibility/ax_event_generator.h"
|
||||
|
||||
+#include "base/feature_list.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "ui/accessibility/ax_enums.mojom.h"
|
||||
#include "ui/accessibility/ax_event.h"
|
||||
@@ -12,6 +13,12 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
+// Feature flag for firing MENU_POPUP_START for dynamically created ARIA menus.
|
||||
+// Disabled by default to allow stabilization before enabling globally.
|
||||
+BASE_FEATURE(kDynamicMenuPopupEvents,
|
||||
+ "DynamicMenuPopupEvents",
|
||||
+ base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
+
|
||||
namespace {
|
||||
|
||||
bool HasEvent(const std::set<AXEventGenerator::EventParams>& node_events,
|
||||
@@ -914,12 +921,31 @@ void AXEventGenerator::OnAtomicUpdateFinished(
|
||||
/*new_value*/ true);
|
||||
}
|
||||
|
||||
- if (IsAlert(change.node->GetRole()))
|
||||
+ if (IsAlert(change.node->GetRole())) {
|
||||
AddEvent(change.node, Event::ALERT);
|
||||
- else if (change.node->data().IsActiveLiveRegionRoot())
|
||||
+ } else if (change.node->data().IsActiveLiveRegionRoot()) {
|
||||
AddEvent(change.node, Event::LIVE_REGION_CREATED);
|
||||
- else if (change.node->data().IsContainedInActiveLiveRegion())
|
||||
+ } else if (change.node->data().IsContainedInActiveLiveRegion()) {
|
||||
FireLiveRegionEvents(change.node, /* is_removal */ false);
|
||||
+ }
|
||||
+
|
||||
+ // Fire MENU_POPUP_START when a menu is dynamically created (e.g. via
|
||||
+ // appendChild). The OnIgnoredChanged path handles menus that already exist
|
||||
+ // in the DOM and are shown/hidden. This handles the case where the menu
|
||||
+ // element itself is created on the fly.
|
||||
+ // Only fire if the menu has at least one menuitem child, to avoid false
|
||||
+ // positives from elements that misuse role="menu".
|
||||
+ if (base::FeatureList::IsEnabled(kDynamicMenuPopupEvents) &&
|
||||
+ change.node->GetRole() == ax::mojom::Role::kMenu &&
|
||||
+ !change.node->IsInvisibleOrIgnored()) {
|
||||
+ for (auto iter = change.node->UnignoredChildrenBegin();
|
||||
+ iter != change.node->UnignoredChildrenEnd(); ++iter) {
|
||||
+ if (IsMenuItem(iter->GetRole())) {
|
||||
+ AddEvent(change.node, Event::MENU_POPUP_START);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
FireActiveDescendantEvents();
|
||||
Reference in New Issue
Block a user