Created
March 19, 2026 19:16
-
-
Save VerteDinde/bc546fd4718d11767169fb199ce56a6d to your computer and use it in GitHub Desktop.
aria-setsize-423673297
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>aria-setsize Repro — crbug.com/423673297</title> | |
| <style> | |
| body { font-family: system-ui; padding: 20px; } | |
| ul { margin: 12px 0; } | |
| li { padding: 4px 0; } | |
| h3 { margin-top: 24px; } | |
| #status { margin-top: 16px; color: #666; font-size: 13px; } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- | |
| Repro for crbug.com/423673297 — aria-setsize="-1" causes VoiceOver | |
| to read "18,446,744,073,709,551,615" instead of ignoring the value. | |
| HOW TO TEST: | |
| 1. Open Accessibility Inspector (Xcode > Open Developer Tool > Accessibility Inspector) | |
| 2. Select this Electron window from the target dropdown | |
| 3. Navigate to the tree items below using the Inspector or VoiceOver (Cmd+F5) | |
| 4. Focus on "Item 1" in the "Bug" section and check its reported set size | |
| EXPECTED (with patch): | |
| VoiceOver says "Item 1 of 3" (or omits set size), because | |
| aria-setsize="-1" means "unknown size" per the ARIA spec. | |
| ACTUAL (without patch): | |
| VoiceOver says "Item 1 of 18,446,744,073,709,551,615" because | |
| -1 is interpreted as UINT64_MAX. | |
| The "Working" section uses aria-setsize="5" as a control to confirm | |
| set size reporting works normally. | |
| --> | |
| <h2>aria-setsize="-1" VoiceOver Bug</h2> | |
| <h3>Bug: aria-setsize="-1" (unknown size)</h3> | |
| <div role="tree" aria-label="Bug Demo Tree"> | |
| <div role="treeitem" aria-level="1" aria-setsize="-1" aria-posinset="1" tabindex="0"> | |
| Item 1 (setsize=-1) | |
| </div> | |
| <div role="treeitem" aria-level="1" aria-setsize="-1" aria-posinset="2" tabindex="-1"> | |
| Item 2 (setsize=-1) | |
| </div> | |
| <div role="treeitem" aria-level="1" aria-setsize="-1" aria-posinset="3" tabindex="-1"> | |
| Item 3 (setsize=-1) | |
| </div> | |
| </div> | |
| <h3>Control: aria-setsize="5" (explicit size)</h3> | |
| <div role="tree" aria-label="Control Demo Tree"> | |
| <div role="treeitem" aria-level="1" aria-setsize="5" aria-posinset="1" tabindex="0"> | |
| Item 1 (setsize=5) | |
| </div> | |
| <div role="treeitem" aria-level="1" aria-setsize="5" aria-posinset="2" tabindex="-1"> | |
| Item 2 (setsize=5) | |
| </div> | |
| <div role="treeitem" aria-level="1" aria-setsize="5" aria-posinset="3" tabindex="-1"> | |
| Item 3 (setsize=5) | |
| </div> | |
| </div> | |
| <h3>Control: no aria-setsize (computed)</h3> | |
| <div role="tree" aria-label="Computed Demo Tree"> | |
| <div role="treeitem" aria-level="1" tabindex="0"> | |
| Item 1 (no setsize) | |
| </div> | |
| <div role="treeitem" aria-level="1" tabindex="-1"> | |
| Item 2 (no setsize) | |
| </div> | |
| <div role="treeitem" aria-level="1" tabindex="-1"> | |
| Item 3 (no setsize) | |
| </div> | |
| </div> | |
| <div id="status"> | |
| <p>Focus tree items with VoiceOver or inspect with Accessibility Inspector to compare set size announcements.</p> | |
| </div> | |
| <script src="./renderer.js"></script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "name": "teeny-side-wash-o57wj", | |
| "productName": "teeny-side-wash-o57wj", | |
| "description": "My Electron application description", | |
| "keywords": [], | |
| "main": "./main.js", | |
| "version": "1.0.0", | |
| "author": "khammond", | |
| "scripts": { | |
| "start": "electron ." | |
| }, | |
| "dependencies": {}, | |
| "devDependencies": { | |
| "electron": "42.0.0-alpha.2" | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * The preload script runs before `index.html` is loaded | |
| * in the renderer. It has access to web APIs as well as | |
| * Electron's renderer process modules and some polyfilled | |
| * Node.js functions. | |
| * | |
| * https://www.electronjs.org/docs/latest/tutorial/sandbox | |
| */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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