Skip to content

Instantly share code, notes, and snippets.

@VerteDinde
Created March 19, 2026 07:35
Show Gist options
  • Select an option

  • Save VerteDinde/81c2e1de58350893627d736b3b038b91 to your computer and use it in GitHub Desktop.

Select an option

Save VerteDinde/81c2e1de58350893627d736b3b038b91 to your computer and use it in GitHub Desktop.
AXMenuOpened-40794596
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>AXMenuOpened Repro — crbug.com/40794596</title>
<style>
body { font-family: system-ui; padding: 20px; }
button { margin: 8px 4px; padding: 8px 16px; font-size: 14px; }
[role="menu"] {
border: 1px solid #999;
padding: 4px 0;
width: 200px;
background: #fff;
margin-top: 8px;
}
[role="menuitem"] {
padding: 6px 16px;
cursor: default;
}
[role="menuitem"]:hover { background: #0078d4; color: #fff; }
#status { margin-top: 16px; color: #666; font-size: 13px; }
</style>
</head>
<body>
<!--
Repro for crbug.com/40794596 — AXMenuOpened not firing for dynamically
created ARIA menus.
HOW TO TEST:
1. Open Accessibility Inspector (Xcode > Open Developer Tool > Accessibility Inspector)
2. Select this Electron window from the target dropdown
3. Open Window > Show Notifications
4. Click "Show Menu" and watch for an AXMenuOpened notification
EXPECTED (with patch):
AXMenuOpened fires when the menu is added to the DOM.
AXMenuClosed fires when the menu is removed.
ACTUAL (without patch):
No AXMenuOpened or AXMenuClosed events fire.
Menus shown via visibility toggle (the other button) DO fire correctly
because that path goes through OnIgnoredChanged.
-->
<h2>AXMenuOpened Repro</h2>
<p>Test dynamically created/removed ARIA menus vs. visibility-toggled menus.</p>
<h3>Dynamic creation (the bug)</h3>
<button id="show-dynamic">Show Menu (appendChild)</button>
<button id="hide-dynamic">Hide Menu (removeChild)</button>
<div id="dynamic-container"></div>
<h3>Visibility toggle (works without patch)</h3>
<button id="toggle-visibility">Toggle Menu (aria-hidden)</button>
<div id="visibility-menu" role="menu" aria-label="Visibility Menu" aria-hidden="true" style="display:none;">
<div role="menuitem">Alpha</div>
<div role="menuitem">Beta</div>
<div role="menuitem">Gamma</div>
</div>
<div id="status"></div>
<script src="./renderer.js"></script>
</body>
</html>
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow () {
const win = new BrowserWindow({
width: 600,
height: 400,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
});
win.loadFile(path.join(__dirname, 'index.html'));
}
app.whenReady().then(createWindow);
{
"name": "graceful-camp-bless-hwv6i",
"productName": "graceful-camp-bless-hwv6i",
"description": "My Electron application description",
"keywords": [],
"main": "./main.js",
"version": "1.0.0",
"author": "khammond",
"scripts": {
"start": "electron ."
},
"dependencies": {},
"devDependencies": {
"electron": "999.999.999"
}
}
// No preload logic needed for this repro.
const container = document.getElementById('dynamic-container');
const status = document.getElementById('status');
function log (msg) {
status.textContent = msg;
console.log(msg);
}
// --- Dynamic creation / removal (the buggy path) ---
document.getElementById('show-dynamic').addEventListener('click', () => {
if (container.querySelector('[role="menu"]')) {
log('Menu already shown.');
return;
}
const menu = document.createElement('div');
menu.setAttribute('role', 'menu');
menu.setAttribute('aria-label', 'Dynamic Menu');
const items = ['Cut', 'Copy', 'Paste'];
for (const label of items) {
const item = document.createElement('div');
item.setAttribute('role', 'menuitem');
item.textContent = label;
menu.appendChild(item);
}
container.appendChild(menu);
log('Dynamic menu added via appendChild. AXMenuOpened should fire.');
});
document.getElementById('hide-dynamic').addEventListener('click', () => {
const menu = container.querySelector('[role="menu"]');
if (menu) {
container.removeChild(menu);
log('Dynamic menu removed via removeChild. AXMenuClosed should fire.');
} else {
log('No dynamic menu to remove.');
}
});
// --- Visibility toggle (the working path) ---
const visMenu = document.getElementById('visibility-menu');
document.getElementById('toggle-visibility').addEventListener('click', () => {
const hidden = visMenu.getAttribute('aria-hidden') === 'true';
visMenu.setAttribute('aria-hidden', String(!hidden));
visMenu.style.display = hidden ? '' : 'none';
log(hidden
? 'Visibility menu shown (aria-hidden=false). AXMenuOpened should fire.'
: 'Visibility menu hidden (aria-hidden=true). AXMenuClosed should fire.');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment