Bug 248717 - AX: Don't include password input value in aria-labelledby description
Summary: AX: Don't include password input value in aria-labelledby description
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Accessibility (show other bugs)
Version: Safari 16
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2022-12-03 12:10 PST by Tommy McHugh
Modified: 2023-03-31 12:16 PDT (History)
11 users (show)

See Also:


Attachments
Patch (6.76 KB, patch)
2022-12-03 12:15 PST, Tommy McHugh
no flags Details | Formatted Diff | Diff
Patch (6.76 KB, patch)
2022-12-03 12:26 PST, Tommy McHugh
no flags Details | Formatted Diff | Diff
Patch (6.52 KB, patch)
2022-12-03 12:45 PST, Tommy McHugh
no flags Details | Formatted Diff | Diff
Patch (7.91 KB, patch)
2022-12-03 13:02 PST, Tommy McHugh
no flags Details | Formatted Diff | Diff
Patch (7.79 KB, patch)
2022-12-06 17:53 PST, Tommy McHugh
no flags Details | Formatted Diff | Diff
Patch (7.88 KB, patch)
2023-03-30 12:11 PDT, Tommy McHugh
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tommy McHugh 2022-12-03 12:10:07 PST
If an element has an aria-labelledby attribute to a password input we should not make the description of that element the value of the password.
Comment 1 Radar WebKit Bug Importer 2022-12-03 12:10:17 PST
<rdar://problem/102938762>
Comment 2 Tommy McHugh 2022-12-03 12:15:57 PST
Created attachment 463871 [details]
Patch
Comment 3 Tommy McHugh 2022-12-03 12:26:19 PST
Created attachment 463872 [details]
Patch
Comment 4 Tyler Wilcock 2022-12-03 12:32:29 PST
Comment on attachment 463872 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=463872&action=review

> LayoutTests/accessibility/aria-labelledby-on-password-input.html:8
> +<div id="content">

I think you can either remove this `id`, or use it to hide the test text before `finishJSTest()` like so:

document.getElementById("content").style.visibility = "hidden";

> LayoutTests/accessibility/aria-labelledby-on-password-input.html:29
> +            await waitFor(() => password1.stringValue.length === "AXValue: hello".length);

Does this work instead of checking the length?

await waitFor(() => password1.stringValue === "AXValue: hello");

> LayoutTests/accessibility/aria-labelledby-on-password-input.html:34
> +            document.getElementById("button-2").setAttribute("aria-labelledBy", "text-1");

I think aria-labelledBy is typically all lowercased:

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby

> LayoutTests/accessibility/aria-labelledby-on-password-input.html:42
> +            output += expect("button2.description", "'AXDescription: This is before my password field This is after my password field'");

Could maybe make the test smaller while still providing the same diagnostics with:

"This is before my password field" -> "Before password"
"This is after my password field" -> "After password"
Comment 5 Tommy McHugh 2022-12-03 12:45:11 PST
Created attachment 463873 [details]
Patch
Comment 6 Tommy McHugh 2022-12-03 12:47:18 PST
(In reply to Tyler Wilcock from comment #4)
> Does this work instead of checking the length?
> 
> await waitFor(() => password1.stringValue === "AXValue: hello");
Unfortunately we can't do this here because we don't return the actual password value here only the text representation being rendered (sometimes ****). Length check seems to be what the other password tests use.
Comment 7 Tommy McHugh 2022-12-03 13:02:15 PST
Created attachment 463874 [details]
Patch
Comment 8 Andres Gonzalez 2022-12-06 05:03:59 PST
(In reply to Tommy McHugh from comment #7)
> Created attachment 463874 [details]
> Patch

--- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp

+    if (auto* input = dynamicDowncast<HTMLInputElement>(element)) {
+        if (input->isPasswordField())
+            return String();

This should return a string with the same length as input->value() consisting of a repeated mask char, e.g., if input->value() is "blah", the return value here should "****".

--- /dev/null
+++ b/LayoutTests/accessibility/aria-labelledby-on-password-input.html

+        setTimeout(async function() {
+            await waitFor(() => password1.stringValue.length === "AXValue: hello".length);
+            output += expect("button1.description", "'AXDescription: '");

+        setTimeout(async function() {
+            await waitFor(() => password1.stringValue.length === "AXValue: hello".length);
+            output += expect("button1.description", "'AXDescription: '");

Should be:

+            output += expect("button1.description", "'AXDescription: *****'");

The same applies to the other instances. In general the value of the password field should not be exposed as an empty string but instead as a mask string, so that the VO user can arrow through it, know how many chars are in the filed, etc., i.e., to have the same experience as the sighted user.
Comment 9 Tommy McHugh 2022-12-06 17:53:53 PST
Created attachment 463911 [details]
Patch
Comment 10 Tommy McHugh 2022-12-06 17:57:48 PST
(In reply to Andres Gonzalez from comment #8)
> The same applies to the other instances. In general the value of the
> password field should not be exposed as an empty string but instead as a
> mask string, so that the VO user can arrow through it, know how many chars
> are in the filed, etc., i.e., to have the same experience as the sighted
> user.
Sounds good! Made this the bullet character mask to match what iOS wrapper uses. AXValue already returns masked characters, it's a VoiceOver specific issue why we don't announce those.
Comment 11 Andres Gonzalez 2022-12-07 06:23:24 PST
(In reply to Tommy McHugh from comment #9)
> Created attachment 463911 [details]
> Patch

--- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp

Can we do a char replacement in place, something like:

+    if (auto* input = dynamicDowncast<HTMLInputElement>(element)) {
+        String inputValue = input->value();
+        if (input->isPasswordField()) {
+            for (size_t i = 0; i < inputValue.length(); i++)
+                inputValue[i] = '•';
+        }
+        return inputValue;
+    }
Comment 12 Tommy McHugh 2022-12-08 11:12:34 PST
(In reply to Andres Gonzalez from comment #11)
> (In reply to Tommy McHugh from comment #9)
> > Created attachment 463911 [details]
> > Patch
> 
> --- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
> +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
> 
> Can we do a char replacement in place, something like:
This doesn't work, WTFString can't assign here for characters. Took a look at header for WTFString and didn't find another method either that would do the equivalent.
Comment 13 Andres Gonzalez 2022-12-13 08:00:56 PST
(In reply to Tommy McHugh from comment #12)
> (In reply to Andres Gonzalez from comment #11)
> > (In reply to Tommy McHugh from comment #9)
> > > Created attachment 463911 [details]
> > > Patch
> > 
> > --- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
> > +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
> > 
> > Can we do a char replacement in place, something like:
> This doesn't work, WTFString can't assign here for characters. Took a look
> at header for WTFString and didn't find another method either that would do
> the equivalent.

this may be more efficient than multiple reallocations and concatenations:

+    if (auto* input = dynamicDowncast<HTMLInputElement>(element)) {
+        String inputValue = input->value();
+        if (input->isPasswordField()) {
+            StringBuilder passwordValue;
+            passwordValue.reserveCapacity(inputValue.length());
+            for (size_t i = 0; i < inputValue.length(); i++)
+                passwordValue.append(''•');
+            return passwordValue.toString();
+        }
+        return inputValue;
+    }
Comment 14 Tommy McHugh 2023-03-30 12:11:54 PDT
Created attachment 465685 [details]
Patch
Comment 15 Tyler Wilcock 2023-03-31 11:47:38 PDT
rdar://102815043
Comment 16 EWS 2023-03-31 12:16:20 PDT
Committed 262433@main (3b6d017ba868): <https://commits.webkit.org/262433@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 465685 [details].