Skip to content

Commit

Permalink
AX: VoiceOver MacOS does not read the web datetime elements in the sa…
Browse files Browse the repository at this point in the history
…me way as other system datetime controls.

https://bugs.webkit.org/show_bug.cgi?id=269864
<rdar://problem/123399237>

Reviewed by Tyler Wilcock.

VoiceOver expects the following properties to be exposed by datetime elements:
- AXRole = AXDateTimeArea
- AXValue = raw date value for the element as an NSDate
- AXDateTimeComponents = NSUInteger representing the component flags that are valid in the raw date value
This patch makes these changes.

* LayoutTests/platform/mac-wk2/accessibility/aria-visible-element-roles-expected.txt:
* LayoutTests/platform/mac-wk2/accessibility/roles-exposed-expected.txt:
* Source/WebCore/accessibility/AXCoreObject.cpp:
(WebCore::AXCoreObject::value):
* Source/WebCore/accessibility/AXCoreObject.h:
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::dateTimeValue const):
* Source/WebCore/accessibility/AccessibilityNodeObject.h:
* Source/WebCore/accessibility/AccessibilityObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::initializeProperties):
(WebCore::AXIsolatedObject::setProperty):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
* Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm:
(WebCore::AccessibilityObject::dateTimeComponents const):
(WebCore::Accessibility::createPlatformRoleMap):
* Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
* Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::stringValue):

Canonical link: https://commits.webkit.org/275176@main
  • Loading branch information
AndresGonzalezApple committed Feb 22, 2024
1 parent e9496d0 commit b73fb5f
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ This test ensures ARIA visible elements (e.g. those with both `hidden` and `aria
AXSubrole: AXToggle

<input hidden="" aria-hidden="false" id="date-input" type="date">
AXRole: AXTextField
AXRole: AXDateTimeArea
computedRoleString: textbox

<input hidden="" aria-hidden="false" id="color-input" type="color">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,19 +209,19 @@ input[type='checkbox']
AXRoleDescription: checkbox

input[type='date']
AXRole: AXTextField
AXRole: AXDateTimeArea
AXSubrole:
AXRoleDescription: date field
AXRoleDescription: date time area

input[type='datetime']
AXRole: AXTextField
AXSubrole:
AXRoleDescription: text field

input[type='datetime-local']
AXRole: AXTextField
AXRole: AXDateTimeArea
AXSubrole:
AXRoleDescription: date and time field
AXRoleDescription: date time area

input[type='email']
AXRole: AXTextField
Expand All @@ -242,9 +242,9 @@ input[type='image']
AXRoleDescription: button

input[type='month']
AXRole: AXTextField
AXRole: AXDateTimeArea
AXSubrole:
AXRoleDescription: month and year field
AXRoleDescription: date time area

input[type='number']
AXRole: AXTextField
Expand Down Expand Up @@ -292,19 +292,19 @@ input[type='text']
AXRoleDescription: text field

input[type='time']
AXRole: AXTextField
AXRole: AXDateTimeArea
AXSubrole:
AXRoleDescription: time field
AXRoleDescription: date time area

input[type='url']
AXRole: AXTextField
AXSubrole:
AXRoleDescription: URL field

input[type='week']
AXRole: AXTextField
AXRole: AXDateTimeArea
AXSubrole:
AXRoleDescription: week and year field
AXRoleDescription: date time area

ins:not([datetime])
AXRole: AXGroup
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/accessibility/AXCoreObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ AXCoreObject::AXValue AXCoreObject::value()
if (isTabItem())
return isSelected();

if (isDateTime())
return dateTimeValue();

if (isColorWell()) {
auto color = convertColor<SRGBA<float>>(colorValue()).resolved();
return makeString("rgb ", String::numberToStringFixedPrecision(color.red, 6, TrailingZerosPolicy::Keep), " ", String::numberToStringFixedPrecision(color.green, 6, TrailingZerosPolicy::Keep), " ", String::numberToStringFixedPrecision(color.blue, 6, TrailingZerosPolicy::Keep), " 1");
Expand Down
7 changes: 6 additions & 1 deletion Source/WebCore/accessibility/AXCoreObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <wtf/ProcessID.h>
#include <wtf/RefCounted.h>
#include <wtf/ThreadSafeWeakPtr.h>
#include <wtf/WallTime.h>

#if PLATFORM(WIN)
#include "AccessibilityObjectWrapperWin.h"
Expand Down Expand Up @@ -989,6 +990,10 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo
virtual bool hasHighlighting() const = 0;
virtual AXTextMarkerRange textInputMarkedTextMarkerRange() const = 0;

virtual WallTime dateTimeValue() const = 0;
#if PLATFORM(MAC)
virtual unsigned dateTimeComponents() const = 0;
#endif
virtual bool supportsDatetimeAttribute() const = 0;
virtual String datetimeAttributeValue() const = 0;

Expand Down Expand Up @@ -1111,7 +1116,7 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo

virtual bool inheritsPresentationalRole() const = 0;

using AXValue = std::variant<bool, unsigned, float, String, AccessibilityButtonState, AXCoreObject*>;
using AXValue = std::variant<bool, unsigned, float, String, WallTime, AccessibilityButtonState, AXCoreObject*>;
AXValue value();

// Accessibility Text
Expand Down
9 changes: 9 additions & 0 deletions Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,15 @@ String AccessibilityNodeObject::stringValue() const
return { };
}

WallTime AccessibilityNodeObject::dateTimeValue() const
{
if (!isDateTime())
return { };

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

SRGBA<uint8_t> AccessibilityNodeObject::colorValue() const
{
#if !ENABLE(INPUT_TYPE_COLOR)
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/accessibility/AccessibilityNodeObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class AccessibilityNodeObject : public AccessibilityObject {
void alternativeText(Vector<AccessibilityText>&) const;
void helpText(Vector<AccessibilityText>&) const;
String stringValue() const override;
WallTime dateTimeValue() const final;
SRGBA<uint8_t> colorValue() const override;
String ariaLabeledByAttribute() const override;
bool hasAttributesRequiredForInclusion() const final;
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/accessibility/AccessibilityObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
bool hasHighlighting() const override;
AXTextMarkerRange textInputMarkedTextMarkerRange() const final;

WallTime dateTimeValue() const override { return { }; }
#if PLATFORM(MAC)
unsigned dateTimeComponents() const override;
#endif
bool supportsDatetimeAttribute() const override;
String datetimeAttributeValue() const override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ void AXIsolatedObject::initializeProperties(const Ref<AccessibilityObject>& axOb
if (object.isListBox())
setObjectVectorProperty(AXPropertyName::VisibleChildren, object.visibleChildren());

if (object.isDateTime()) {
setProperty(AXPropertyName::DateTimeValue, object.dateTimeValue().isolatedCopy());
#if PLATFORM(MAC)
setProperty(AXPropertyName::DateTimeComponents, object.dateTimeComponents());
#endif
}

if (object.isSpinButton()) {
// FIXME: These properties get out of date every time AccessibilitySpinButton::{clearChildren, addChildren} is called. We should probably just not cache these properties.
setObjectProperty(AXPropertyName::DecrementButton, object.decrementButton());
Expand Down Expand Up @@ -479,6 +486,7 @@ void AXIsolatedObject::setProperty(AXPropertyName propertyName, AXPropertyValueV
#if ENABLE(AX_THREAD_TEXT_APIS)
[](AXTextRuns& runs) { return !runs.size(); },
#endif
[] (WallTime& time) { return !time; },
[](auto&) {
ASSERT_NOT_REACHED();
return false;
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ class AXIsolatedObject final : public AXCoreObject {
#endif
IntSize size() const final { return snappedIntRect(LayoutRect(relativeFrame())).size(); }
FloatRect relativeFrameFromChildren() const;
WallTime dateTimeValue() const { return propertyValue<WallTime>(AXPropertyName::DateTimeValue); }
#if PLATFORM(MAC)
unsigned dateTimeComponents() const { return propertyValue<unsigned>(AXPropertyName::DateTimeComponents); }
#endif
bool supportsDatetimeAttribute() const final { return boolAttributeValue(AXPropertyName::SupportsDatetimeAttribute); }
String datetimeAttributeValue() const final { return stringAttributeValue(AXPropertyName::DatetimeAttributeValue); }
bool canSetValueAttribute() const final { return boolAttributeValue(AXPropertyName::CanSetValueAttribute); }
Expand Down
6 changes: 5 additions & 1 deletion Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ enum class AXPropertyName : uint16_t {
ColumnIndex,
ColumnIndexRange,
CurrentState,
#if PLATFORM(MAC)
DateTimeComponents,
#endif
DateTimeValue,
DatetimeAttributeValue,
DecrementButton,
Description,
Expand Down Expand Up @@ -257,7 +261,7 @@ enum class AXPropertyName : uint16_t {
using AXPropertyNameSet = HashSet<AXPropertyName, IntHash<AXPropertyName>, WTF::StrongEnumHashTraits<AXPropertyName>>;

// If this type is modified, the switchOn statment in AXIsolatedObject::setProperty must be updated as well.
using AXPropertyValueVariant = std::variant<std::nullptr_t, AXID, String, bool, int, unsigned, double, float, uint64_t, AccessibilityButtonState, Color, URL, LayoutRect, FloatPoint, FloatRect, IntPoint, IntRect, std::pair<unsigned, unsigned>, Vector<AccessibilityText>, Vector<AXID>, Vector<std::pair<AXID, AXID>>, Vector<String>, Path, OptionSet<AXAncestorFlag>, InsideLink, Vector<Vector<AXID>>, CharacterRange, std::pair<AXID, CharacterRange>
using AXPropertyValueVariant = std::variant<std::nullptr_t, AXID, String, bool, int, unsigned, double, float, uint64_t, WallTime, AccessibilityButtonState, Color, URL, LayoutRect, FloatPoint, FloatRect, IntPoint, IntRect, std::pair<unsigned, unsigned>, Vector<AccessibilityText>, Vector<AXID>, Vector<std::pair<AXID, AXID>>, Vector<String>, Path, OptionSet<AXAncestorFlag>, InsideLink, Vector<Vector<AXID>>, CharacterRange, std::pair<AXID, CharacterRange>
#if PLATFORM(COCOA)
, RetainPtr<NSAttributedString>
, RetainPtr<id>
Expand Down
41 changes: 40 additions & 1 deletion Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#import "ColorCocoa.h"
#import "CompositionHighlight.h"
#import "CompositionUnderline.h"
#import "DateComponents.h"
#import "Editor.h"
#import "ElementAncestorIteratorInlines.h"
#import "FrameSelection.h"
Expand Down Expand Up @@ -511,6 +512,44 @@
return String();
}

// VO requests a bit-wise combination of these constants via the API
// AXDateTimeComponents to determine which fields of a datetime value are presented to the user.
typedef NS_OPTIONS(NSUInteger, AXFDateTimeComponent) {
AXFDateTimeComponentSeconds = 0x0002,
AXFDateTimeComponentMinutes = 0x0004,
AXFDateTimeComponentHours = 0x0008,
AXFDateTimeComponentDays = 0x0020,
AXFDateTimeComponentMonths = 0x0040,
AXFDateTimeComponentYears = 0x0080,
AXFDateTimeComponentEras = 0x0100
};

unsigned AccessibilityObject::dateTimeComponents() const
{
if (!isDateTime())
return 0;

auto* input = dynamicDowncast<HTMLInputElement>(node());
if (!input)
return 0;

switch (input->dateType()) {
case DateComponentsType::Invalid:
return 0;
case DateComponentsType::Date:
return AXFDateTimeComponentDays | AXFDateTimeComponentMonths | AXFDateTimeComponentYears;
case DateComponentsType::DateTimeLocal:
return AXFDateTimeComponentSeconds | AXFDateTimeComponentMinutes | AXFDateTimeComponentHours
| AXFDateTimeComponentDays | AXFDateTimeComponentMonths | AXFDateTimeComponentYears;
case DateComponentsType::Month:
return AXFDateTimeComponentMonths;
case DateComponentsType::Time:
return AXFDateTimeComponentSeconds | AXFDateTimeComponentMinutes | AXFDateTimeComponentHours;
case DateComponentsType::Week:
return 0;
};
}

// NSAttributedString support.

static void attributedStringSetColor(NSMutableAttributedString *attrString, NSString *attribute, NSColor *color, const NSRange& range)
Expand Down Expand Up @@ -819,7 +858,7 @@ PlatformRoleMap createPlatformRoleMap()
{ AccessibilityRole::Meter, NSAccessibilityLevelIndicatorRole },
{ AccessibilityRole::Incrementor, NSAccessibilityIncrementorRole },
{ AccessibilityRole::ComboBox, NSAccessibilityComboBoxRole },
{ AccessibilityRole::DateTime, NSAccessibilityTextFieldRole },
{ AccessibilityRole::DateTime, @"AXDateTimeArea" },
{ AccessibilityRole::Splitter, NSAccessibilitySplitterRole },
{ AccessibilityRole::Code, NSAccessibilityGroupRole },
{ AccessibilityRole::ColorWell, NSAccessibilityColorWellRole },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1691,7 +1691,7 @@ - (id)accessibilityAttributeValue:(NSString*)attributeName
return backingObject->descriptionAttributeValue();
}

if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
if ([attributeName isEqualToString:NSAccessibilityValueAttribute]) {
if (backingObject->isAttachment()) {
id attachmentView = [self attachmentView];
if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute])
Expand All @@ -1704,12 +1704,19 @@ - (id)accessibilityAttributeValue:(NSString*)attributeName
[] (unsigned& typedValue) -> id { return @(typedValue); },
[] (float& typedValue) -> id { return @(typedValue); },
[] (String& typedValue) -> id { return (NSString *)typedValue; },
[] (WallTime& typedValue) -> id {
RetainPtr date = [NSDate dateWithTimeIntervalSince1970:typedValue.secondsSinceEpoch().value()];
return date.autorelease();
},
[] (AccessibilityButtonState& typedValue) -> id { return @((unsigned)typedValue); },
[] (AXCoreObject*& typedValue) { return typedValue ? (id)typedValue->wrapper() : nil; },
[] (auto&) { return nil; }
);
}

if ([attributeName isEqualToString:@"AXDateTimeComponents"])
return @(backingObject->dateTimeComponents());

if ([attributeName isEqualToString:(NSString *)kAXMenuItemMarkCharAttribute]) {
const unichar ch = 0x2713; // ✓ used on Mac for selected menu items.
return (backingObject->isChecked()) ? [NSString stringWithCharacters:&ch length:1] : nil;
Expand Down Expand Up @@ -2268,6 +2275,9 @@ - (id)accessibilityAttributeValue:(NSString*)attributeName
if (UNLIKELY([attributeName isEqualToString:@"AXARIARole"]))
return backingObject->computedRoleString();

if (UNLIKELY([attributeName isEqualToString:@"AXStringValue"]))
return backingObject->stringValue();

if (UNLIKELY([attributeName isEqualToString:@"AXControllers"]))
return makeNSArray(backingObject->controllers());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,13 @@ void setAttributeValue(id element, NSString* attribute, id value, bool synchrono
JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
{
BEGIN_AX_OBJC_EXCEPTIONS
auto value = attributeValue(NSAccessibilityValueAttribute);
RetainPtr<id> value;
auto role = attributeValue(NSAccessibilityRoleAttribute);
if ([role isEqualToString:@"AXDateTimeArea"])
value = attributeValue(@"AXStringValue");
else
value = attributeValue(NSAccessibilityValueAttribute);

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

0 comments on commit b73fb5f

Please sign in to comment.