Skip to content

Commit

Permalink
AX: VoiceOver reads erroneous value for datetime-local input elements.
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=270276
<rdar://problem/123803281>

Reviewed by Tyler Wilcock.

AccessibilityNodeObject::dateTimeValue() relied on HTMLInputElement::valueAsDate(), that in turn forwards to an override of InputType::valueAsDate. The problem is that DateTimeLocalInputType::valueAsDate returns NaN unlike the overrides in the other subclasses of BaseDateAndTimeInputType. To solve this problem, we added the method accessibilityValueAsDate() that returns the right value for all BaseDateAndTimeInputType subclasses including DateTimeLocalInputType.
Added the AccessibilityUIElement::dateValue() method to the TestRunner to exercise this functionality. The new test does just that for datetime-local input elements.

* LayoutTests/accessibility/datetime/input-datetime-local-label-value-expected.txt: Added.
* LayoutTests/accessibility/datetime/input-datetime-local-label-value.html: Added.
* LayoutTests/platform/glib/TestExpectations:
* LayoutTests/platform/mac-monterey/accessibility/datetime/input-datetime-local-label-value-expected.txt: Added.
* LayoutTests/platform/mac-wk1/TestExpectations:
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::dateTimeValue const):
* Source/WebCore/html/BaseDateAndTimeInputType.cpp:
(WebCore::BaseDateAndTimeInputType::accessibilityValueAsDate const):
* Source/WebCore/html/BaseDateAndTimeInputType.h:
* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::accessibilityValueAsDate const):
* Source/WebCore/html/HTMLInputElement.h:
* Source/WebCore/html/InputType.cpp:
(WebCore::InputType::accessibilityValueAsDate const):
* Source/WebCore/html/InputType.h:
* Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
* Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
* Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::stringValue):
(WTR::AccessibilityUIElement::dateValue):

Canonical link: https://commits.webkit.org/275548@main
  • Loading branch information
AndresGonzalezApple committed Mar 1, 2024
1 parent 4c3c883 commit 1377080
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
This tests input type=datetime-local label and value properties.

datetime1:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime2:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime3:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime4:
PASS: datetime.title === 'AXTitle: '
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000


PASS successfullyParsed is true

TEST COMPLETE
Meeting time: Meeting time:
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/accessibility-helper.js"></script>
<script src="../../resources/js-test.js"></script>
</head>
<body>

<input type="datetime-local" value="2024-10-21T15:45:59" aria-label="Meeting time:" id="datetime1" />

<label for="datetime2">Meeting time:</label>
<input type="datetime-local" value="2024-10-21T15:45:59" id="datetime2" />

<label>
Meeting time:
<input type="datetime-local" value="2024-10-21T15:45:59" id="datetime3" />
</label>

<input type="datetime-local" value="2024-10-21T15:45:59" id="datetime4" />

<script>
let output = "This tests input type=datetime-local label and value properties.\n\n";

if (window.accessibilityController) {
var datetime = null;
function outputDateTimeElement(id) {
datetime = accessibilityController.accessibleElementById(id);
output += `${datetime.domIdentifier}:\n`;
output += expect("datetime.title", "'AXTitle: Meeting time:'");
output += `${datetime.stringValue}\n`;
output += `${datetime.dateValue}\n\n`;
}

for (let i = 1; i <= 3; ++i)
outputDateTimeElement(`datetime${i}`);

// Case 4: no label.
datetime = accessibilityController.accessibleElementById("datetime4");
output += "datetime4:\n";
output += expect("datetime.title", "'AXTitle: '");
output += `${datetime.stringValue}\n`;
output += `${datetime.dateValue}\n\n`;

debug(output);
}
</script>
</body>
</html>
5 changes: 2 additions & 3 deletions LayoutTests/platform/glib/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,8 @@ accessibility/display-contents/tree-and-treeitems.html [ Skip ]

webkit.org/b/212805 accessibility/svg-text.html [ Failure ]

webkit.org/b/251544 accessibility/datetime/input-date-field-labels-and-value-changes.html [ Skip ]
accessibility/datetime/input-time-field-labels-and-value-changes.html [ Skip ]
accessibility/datetime/input-time-label-value.html [ Skip ]
# AX support for datetime input elements needs to be completed in glib platforms.
accessibility/datetime [ Skip ]

# Added in r263823. Both tests are timing out.
webkit.org/b/213874 accessibility/keyevents-for-increment-actions-with-node-removal.html [ Skip ]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
This tests input type=datetime-local label and value properties.

datetime1:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime2:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime3:
PASS: datetime.title === 'AXTitle: Meeting time:'
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000

datetime4:
PASS: datetime.title === 'AXTitle: '
AXValue: Oct 21, 2024 at 3:45 PM
AXDateValue: 2024-10-21 15:45:59 +0000


PASS successfullyParsed is true

TEST COMPLETE
Meeting time: Meeting time:
4 changes: 1 addition & 3 deletions LayoutTests/platform/mac-wk1/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -816,9 +816,7 @@ fast/speechrecognition/start-recognition-after-gum.html [ Skip ]
fast/speechrecognition/start-recognition-after-denied-gum.html [ Skip ]
fast/speechrecognition/start-recognition-without-devices.html [ Skip ]

accessibility/datetime/input-date-field-labels-and-value-changes.html [ WontFix ]
accessibility/datetime/input-time-field-labels-and-value-changes.html [ WontFix ]
accessibility/datetime/input-time-label-value.html [ WontFix ]
accessibility/datetime [ WontFix ]
accessibility/announcement-notification.html [ WontFix ]
accessibility/menuitem-is-selected.html [ WontFix ]
accessibility/relationships.html [ WontFix ]
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2482,7 +2482,7 @@ WallTime AccessibilityNodeObject::dateTimeValue() const
return { };

auto* input = dynamicDowncast<HTMLInputElement>(node());
return input ? input->valueAsDate() : WallTime();
return input ? input->accessibilityValueAsDate() : WallTime();
}

SRGBA<uint8_t> AccessibilityNodeObject::colorValue() const
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/html/BaseDateAndTimeInputType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ ExceptionOr<void> BaseDateAndTimeInputType::setValueAsDate(WallTime value) const
return { };
}

WallTime BaseDateAndTimeInputType::accessibilityValueAsDate() const
{
return WallTime::fromRawSeconds(Seconds::fromMilliseconds(valueAsDouble()).value());
}

double BaseDateAndTimeInputType::valueAsDouble() const
{
ASSERT(element());
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/html/BaseDateAndTimeInputType.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class BaseDateAndTimeInputType : public InputType, private DateTimeChooserClient
void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final;
WallTime valueAsDate() const override;
ExceptionOr<void> setValueAsDate(WallTime) const override;
WallTime accessibilityValueAsDate() const final;
double valueAsDouble() const final;
ExceptionOr<void> setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const final;
Decimal defaultValueForStepUp() const override;
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/html/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,11 @@ ExceptionOr<void> HTMLInputElement::setValueAsDate(WallTime value)
return m_inputType->setValueAsDate(value);
}

WallTime HTMLInputElement::accessibilityValueAsDate() const
{
return m_inputType->accessibilityValueAsDate();
}

double HTMLInputElement::valueAsNumber() const
{
return m_inputType->valueAsDouble();
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/html/HTMLInputElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class HTMLInputElement final : public HTMLTextFormControlElement {
void setValueForUser(const String& value) { setValue(value, DispatchInputAndChangeEvent); }
WEBCORE_EXPORT WallTime valueAsDate() const;
WEBCORE_EXPORT ExceptionOr<void> setValueAsDate(WallTime);
WallTime accessibilityValueAsDate() const;
WEBCORE_EXPORT double valueAsNumber() const;
WEBCORE_EXPORT ExceptionOr<void> setValueAsNumber(double, TextFieldEventBehavior = DispatchNoEvent);
WEBCORE_EXPORT ExceptionOr<void> stepUp(int = 1);
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/html/InputType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ ExceptionOr<void> InputType::setValueAsDate(WallTime) const
return Exception { ExceptionCode::InvalidStateError };
}

WallTime InputType::accessibilityValueAsDate() const
{
return WallTime::nan();
}

double InputType::valueAsDouble() const
{
return std::numeric_limits<double>::quiet_NaN();
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/html/InputType.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ class InputType : public RefCounted<InputType> {
virtual String defaultValue() const; // Checked after even fallbackValue, only when the valueWithDefault function is called.
virtual WallTime valueAsDate() const;
virtual ExceptionOr<void> setValueAsDate(WallTime) const;
virtual WallTime accessibilityValueAsDate() const;
virtual double valueAsDouble() const;
virtual ExceptionOr<void> setValueAsDouble(double, TextFieldEventBehavior) const;
virtual ExceptionOr<void> setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ bool AccessibilityUIElement::isOnScreen() const { return true; }
JSValueRef AccessibilityUIElement::mathRootRadicand() const { return { }; }
unsigned AccessibilityUIElement::numberOfCharacters() const { return 0; }
JSValueRef AccessibilityUIElement::columns() { return { }; }
JSRetainPtr<JSStringRef> AccessibilityUIElement::dateValue() { return nullptr; }
#endif // !PLATFORM(MAC))

#if !PLATFORM(COCOA)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class AccessibilityUIElement : public JSWrappable {
JSRetainPtr<JSStringRef> description();
JSRetainPtr<JSStringRef> language();
JSRetainPtr<JSStringRef> stringValue();
JSRetainPtr<JSStringRef> dateValue();
JSRetainPtr<JSStringRef> accessibilityValue() const;
JSRetainPtr<JSStringRef> helpText() const;
JSRetainPtr<JSStringRef> orientation() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ interface AccessibilityUIElement {
undefined setValue(DOMString value);

readonly attribute DOMString stringValue;
readonly attribute DOMString dateValue;
readonly attribute long intValue;
readonly attribute long minValue;
readonly attribute long maxValue;

readonly attribute boolean isAtomicLiveRegion;
readonly attribute boolean isBusy;
readonly attribute boolean isEnabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -899,14 +899,29 @@ void setAttributeValue(id element, NSString* attribute, id value, bool synchrono
else
value = attributeValue(NSAccessibilityValueAttribute);

auto description = descriptionOfValue(value.get());
if (description)
if (auto description = descriptionOfValue(value.get()))
return concatenateAttributeAndValue(@"AXValue", description.get());
END_AX_OBJC_EXCEPTIONS

return nullptr;
}

JSRetainPtr<JSStringRef> AccessibilityUIElement::dateValue()
{
BEGIN_AX_OBJC_EXCEPTIONS
auto value = attributeValue(NSAccessibilityValueAttribute);
if (![value isKindOfClass:[NSDate class]])
return nullptr;

NSInteger offset = [[NSTimeZone localTimeZone] secondsFromGMTForDate:[NSDate date]];
value = [NSDate dateWithTimeInterval:offset sinceDate:value.get()];
if (auto description = descriptionOfValue(value.get()))
return concatenateAttributeAndValue(@"AXDateValue", description.get());
END_AX_OBJC_EXCEPTIONS

return nullptr;
}

JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
{
BEGIN_AX_OBJC_EXCEPTIONS
Expand Down

0 comments on commit 1377080

Please sign in to comment.