[go: nahoru, domu]

Prevent screen reader from announcing checkbox label twice

Due to duplicate labeling from the aria-label of the checkbox and from the label element's for attribute, screen readers announce the checkbox label twice. This patch prevents that by removing the aria-label on the checkbox and instead setting a title attribute.

Note the for attribute on the checkbox label was last added in this CL - https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2077666
to address a regression introduced here - https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2062603.

There's a bit of friction with this change because it requires updating existing tests that depend on the checkbox aria-label selector. Future tests would also need to align with this change.

There was an initial attempt to land a similar change but then that got reverted in here - https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3948895

Nonetheless, having both an aria-label on the checkbox and the for attribute on the label will cause the duplicate screen reader announcement.


Bug: 1414952
Change-Id: If4342b860d07b0a9494b90f4d15cb99688cb9d6e
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4591249
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Sylvester Elorm Coch <elormcoch@microsoft.com>
diff --git a/front_end/ui/legacy/UIUtils.ts b/front_end/ui/legacy/UIUtils.ts
index 8cb0ce8..60ead61 100644
--- a/front_end/ui/legacy/UIUtils.ts
+++ b/front_end/ui/legacy/UIUtils.ts
@@ -1201,7 +1201,7 @@
     element.checkboxElement.checked = Boolean(checked);
     if (title !== undefined) {
       element.textElement.textContent = title;
-      ARIAUtils.setAccessibleName(element.checkboxElement, title);
+      element.checkboxElement.title = title;
       if (subtitle !== undefined) {
         element.textElement.createChild('div', 'dt-checkbox-subtitle').textContent = subtitle;
       }
diff --git a/test/e2e/application/cookies_test.ts b/test/e2e/application/cookies_test.ts
index 1e8052a..3a9f226 100644
--- a/test/e2e/application/cookies_test.ts
+++ b/test/e2e/application/cookies_test.ts
@@ -119,7 +119,7 @@
       return previewValue === 'Hello%2BWorld!';
     });
 
-    await click('[aria-label="Show URL-decoded"]');
+    await click('[title="Show URL-decoded"]');
 
     await waitForFunction(async () => {
       const previewValueNode = await waitFor('.cookie-preview-widget-cookie-value');
diff --git a/test/e2e/application/storage_test.ts b/test/e2e/application/storage_test.ts
index a0ea47c..336ce24 100644
--- a/test/e2e/application/storage_test.ts
+++ b/test/e2e/application/storage_test.ts
@@ -20,7 +20,7 @@
 const COOKIES_SELECTOR = '[aria-label="Cookies"].parent';
 const STORAGE_SELECTOR = '[aria-label="Storage"]';
 const CLEAR_SITE_DATA_BUTTON_SELECTOR = '#storage-view-clear-button';
-const INCLUDE_3RD_PARTY_COOKIES_SELECTOR = '[aria-label="including third-party cookies"]';
+const INCLUDE_3RD_PARTY_COOKIES_SELECTOR = '[title="including third-party cookies"]';
 
 let DOMAIN_SELECTOR: string;
 
diff --git a/test/e2e/helpers/console-helpers.ts b/test/e2e/helpers/console-helpers.ts
index 05ceabcd..88f78cf 100644
--- a/test/e2e/helpers/console-helpers.ts
+++ b/test/e2e/helpers/console-helpers.ts
@@ -40,9 +40,9 @@
 export const CONSOLE_MESSAGE_WRAPPER_SELECTOR = '.console-group-messages .console-message-wrapper';
 export const CONSOLE_SELECTOR = '.console-user-command-result';
 export const CONSOLE_SETTINGS_SELECTOR = '[aria-label^="Console settings"]';
-export const AUTOCOMPLETE_FROM_HISTORY_SELECTOR = '[aria-label^="Autocomplete from history"]';
-export const SHOW_CORS_ERRORS_SELECTOR = '[aria-label^="Show CORS errors in console"]';
-export const LOG_XML_HTTP_REQUESTS_SELECTOR = 'input[aria-label="Log XMLHttpRequests"]';
+export const AUTOCOMPLETE_FROM_HISTORY_SELECTOR = '[title="Autocomplete from history"]';
+export const SHOW_CORS_ERRORS_SELECTOR = '[title="Show CORS errors in console"]';
+export const LOG_XML_HTTP_REQUESTS_SELECTOR = '[title="Log XMLHttpRequests"]';
 export const CONSOLE_CREATE_LIVE_EXPRESSION_SELECTOR = '[aria-label^="Create live expression"]';
 
 export const Level = {
diff --git a/test/e2e/helpers/elements-helpers.ts b/test/e2e/helpers/elements-helpers.ts
index c007b58..577a1a6 100644
--- a/test/e2e/helpers/elements-helpers.ts
+++ b/test/e2e/helpers/elements-helpers.ts
@@ -32,8 +32,8 @@
 const CSS_STYLE_RULE_SELECTOR = '[aria-label*="css selector"]';
 const COMPUTED_PROPERTY_SELECTOR = 'devtools-computed-style-property';
 const COMPUTED_STYLES_PANEL_SELECTOR = '[aria-label="Computed panel"]';
-const COMPUTED_STYLES_SHOW_ALL_SELECTOR = '[aria-label="Show all"]';
-const COMPUTED_STYLES_GROUP_SELECTOR = '[aria-label="Group"]';
+const COMPUTED_STYLES_SHOW_ALL_SELECTOR = '[title="Show all"]';
+const COMPUTED_STYLES_GROUP_SELECTOR = '[title="Group"]';
 const ELEMENTS_PANEL_SELECTOR = '.panel[aria-label="elements"]';
 const FONT_EDITOR_SELECTOR = '[aria-label="Font Editor"]';
 const HIDDEN_FONT_EDITOR_SELECTOR = '.font-toolbar-hidden';
@@ -740,7 +740,7 @@
   const initialValue = await getContentOfSelectedNode();
 
   const classesPane = await waitFor(CLS_PANE_SELECTOR);
-  await click(`input[aria-label="${checkboxLabel}"]`, {root: classesPane});
+  await click(`[title="${checkboxLabel}"]`, {root: classesPane});
 
   await waitForSelectedNodeChange(initialValue);
 };
diff --git a/test/e2e/helpers/issues-helpers.ts b/test/e2e/helpers/issues-helpers.ts
index 6a87e67..c1aa67f 100644
--- a/test/e2e/helpers/issues-helpers.ts
+++ b/test/e2e/helpers/issues-helpers.ts
@@ -20,8 +20,8 @@
 export const CATEGORY = '.issue-category:not(.hidden-issues)';
 export const KIND = '.issue-kind';
 export const CATEGORY_NAME = '.issue-category .title';
-export const CATEGORY_CHECKBOX = 'input[aria-label="Group by category"]';
-export const KIND_CHECKBOX = 'input[aria-label="Group by kind"]';
+export const CATEGORY_CHECKBOX = '[title^="Group displayed issues under"]';
+export const KIND_CHECKBOX = '[title^="Group displayed issues as"]';
 export const ISSUE = '.issue:not(.hidden-issue)';
 export const ISSUE_TITLE = '.issue .title';
 export const AFFECTED_ELEMENT_ICON = '.affected-resource-csp-info-node';
diff --git a/test/e2e/helpers/memory-helpers.ts b/test/e2e/helpers/memory-helpers.ts
index 6ca07ec..7ddb037 100644
--- a/test/e2e/helpers/memory-helpers.ts
+++ b/test/e2e/helpers/memory-helpers.ts
@@ -45,7 +45,7 @@
   const radioButton = await $('//label[text()="Allocation instrumentation on timeline"]', undefined, 'xpath');
   await clickElement(radioButton);
   if (recordStacks) {
-    await click('input[aria-label="Record stack traces of allocations (extra performance overhead)"]');
+    await click('[title="Record stack traces of allocations (extra performance overhead)"]');
   }
   await click('button[aria-label="Start recording heap profile"]');
   await new Promise(r => setTimeout(r, 200));
diff --git a/test/e2e/helpers/network-helpers.ts b/test/e2e/helpers/network-helpers.ts
index 6d8765f..2087863 100644
--- a/test/e2e/helpers/network-helpers.ts
+++ b/test/e2e/helpers/network-helpers.ts
@@ -93,11 +93,11 @@
 }
 
 export async function setPersistLog(persist: boolean) {
-  await setCheckBox('[aria-label="Preserve log"]', persist);
+  await setCheckBox('[title="Do not clear log on page reload / navigation"]', persist);
 }
 
 export async function setCacheDisabled(disabled: boolean): Promise<void> {
-  await setCheckBox('[aria-label="Disable cache"]', disabled);
+  await setCheckBox('[title^="Disable cache"]', disabled);
 }
 
 export async function setTimeWindow(): Promise<void> {
diff --git a/test/e2e/helpers/settings-helpers.ts b/test/e2e/helpers/settings-helpers.ts
index 3070b22..99624e0 100644
--- a/test/e2e/helpers/settings-helpers.ts
+++ b/test/e2e/helpers/settings-helpers.ts
@@ -81,7 +81,7 @@
   const enabledPattern = '.ignore-list-options:not(.ignore-listing-disabled)';
   const disabledPattern = '.ignore-list-options.ignore-listing-disabled';
   await waitFor(enable ? disabledPattern : enabledPattern);
-  await click('[aria-label="Enable Ignore Listing"]');
+  await click('[title="Enable Ignore Listing"]');
   await waitFor(enable ? enabledPattern : disabledPattern);
   await closeSettings();
 };
diff --git a/test/e2e/host/user-metrics_test.ts b/test/e2e/host/user-metrics_test.ts
index 20c5a42..83340b9 100644
--- a/test/e2e/host/user-metrics_test.ts
+++ b/test/e2e/host/user-metrics_test.ts
@@ -313,7 +313,7 @@
 
   it('dispatches an event when experiments are enabled and disabled', async () => {
     await openSettingsTab('Experiments');
-    const customThemeCheckbox = await waitFor('[aria-label="Allow extensions to load custom stylesheets"]');
+    const customThemeCheckbox = await waitFor('[title="Allow extensions to load custom stylesheets"]');
     // Enable the experiment
     await customThemeCheckbox.click();
     // Disable the experiment
diff --git a/test/e2e/issues/issue-view-caching_test.ts b/test/e2e/issues/issue-view-caching_test.ts
index 022c718..74bb7fe 100644
--- a/test/e2e/issues/issue-view-caching_test.ts
+++ b/test/e2e/issues/issue-view-caching_test.ts
@@ -85,7 +85,7 @@
       'false',
     ];
     await waitForResources(2, [header, expectedRow1, expectedRow2]);
-    await setCheckBox('[aria-label="Include third-party cookie issues"]', true);
+    await setCheckBox('[title="Include cookie Issues caused by third-party sites"]', true);
 
     // Trigger issue again to see if resources are updated.
     await triggerIssue();
diff --git a/test/e2e/network/network-datagrid_test.ts b/test/e2e/network/network-datagrid_test.ts
index 2bf95e7..3f38fb3 100644
--- a/test/e2e/network/network-datagrid_test.ts
+++ b/test/e2e/network/network-datagrid_test.ts
@@ -65,9 +65,9 @@
     await navigateToNetworkTab('resources-from-cache.html');
 
     // Click the label next to the checkbox input element
-    await click('[aria-label="Disable cache"] + label');
+    await click('[title^="Disable cache"] + label');
 
-    const checkbox = await waitFor('[aria-label="Disable cache"]');
+    const checkbox = await waitFor('[title^="Disable cache"]');
     const checked = await checkbox.evaluate(box => (box as HTMLInputElement).checked);
 
     assert.strictEqual(checked, false, 'The disable cache checkbox should be unchecked');
diff --git a/test/e2e/rendering/Rendering_test.ts b/test/e2e/rendering/Rendering_test.ts
index 6352ae2..25b790d 100644
--- a/test/e2e/rendering/Rendering_test.ts
+++ b/test/e2e/rendering/Rendering_test.ts
@@ -4,7 +4,7 @@
 
 import {assert} from 'chai';
 
-import {getBrowserAndPages, waitFor, waitForAria} from '../../shared/helper.js';
+import {getBrowserAndPages, waitFor} from '../../shared/helper.js';
 import {describe, it} from '../../shared/mocha-extensions.js';
 import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
 
@@ -76,6 +76,6 @@
 
   it('includes UI for emulating auto dark mode', async () => {
     await openPanelViaMoreTools('Rendering');
-    await waitForAria('Enable automatic dark mode[role="checkbox"]');
+    await waitFor('[title="Enable automatic dark mode"]');
   });
 });
diff --git a/test/e2e/sources/breakpoint-csp-violations_test.ts b/test/e2e/sources/breakpoint-csp-violations_test.ts
index 86c64c1..f2a59ed 100644
--- a/test/e2e/sources/breakpoint-csp-violations_test.ts
+++ b/test/e2e/sources/breakpoint-csp-violations_test.ts
@@ -13,7 +13,7 @@
     await openSourcesPanel();
     await waitForAria('CSP Violation Breakpoints');
     await click('[aria-label="CSP Violation Breakpoints"]');
-    await click('[aria-label="Trusted Type Violations"]');
+    await click('[title="Trusted Type Violations"]');
     await click(PAUSE_ON_UNCAUGHT_EXCEPTION_SELECTOR);
 
     const resource = goToResource('network/trusted-type-violations-enforced.rawresponse');
@@ -37,7 +37,7 @@
     await openSourcesPanel();
     await waitForAria('CSP Violation Breakpoints');
     await click('[aria-label="CSP Violation Breakpoints"]');
-    await click('[aria-label="Trusted Type Violations"]');
+    await click('[title="Trusted Type Violations"]');
 
     const resource = goToResource('network/trusted-type-violations-report-only.rawresponse');
 
diff --git a/test/unittests/front_end/panels/application/StorageView_test.ts b/test/unittests/front_end/panels/application/StorageView_test.ts
index 4c30e55..063bdba 100644
--- a/test/unittests/front_end/panels/application/StorageView_test.ts
+++ b/test/unittests/front_end/panels/application/StorageView_test.ts
@@ -76,7 +76,7 @@
       assertElement(container, HTMLDivElement);
       assertShadowRoot(container.shadowRoot);
       const customQuotaCheckbox = container.shadowRoot.querySelector('.quota-override-row span')
-                                      ?.shadowRoot?.querySelector('[aria-label="Simulate custom storage quota"]') ||
+                                      ?.shadowRoot?.querySelector('[title="Simulate custom storage quota"]') ||
           null;
       assertElement(customQuotaCheckbox, HTMLInputElement);
       customQuotaCheckbox.checked = true;