Skip to content

Commit

Permalink
AXAPI: Expose aria-description in accessibilityCustomContent
Browse files Browse the repository at this point in the history
`aria-description` is exposed in accessibilityCustomContent
with label="description" beginning with macOS 11.

Before macOS 11:
`aria-description` will be exposed in AXHelp, which is how it
was exposed before this change.

Specification: w3c/core-aam#142

Change-Id: Iddedce1b696e05542628fa3d471eac2f63c78d0f
Fixed: 1350145

Cq-Include-Trybots: luci.chromium.try:mac10.13-blink-rel,mac10.15-blink-rel
Change-Id: Iddedce1b696e05542628fa3d471eac2f63c78d0f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4015695
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Valerie Young <spectranaut@igalia.com>
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084927}
  • Loading branch information
spectranaut authored and Chromium LUCI CQ committed Dec 19, 2022
1 parent 110fb92 commit 1c4e63e
Show file tree
Hide file tree
Showing 20 changed files with 177 additions and 24 deletions.
Expand Up @@ -17,6 +17,10 @@
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif

namespace content {

using ui::AXPropertyFilter;
Expand All @@ -28,6 +32,7 @@ using ui::AXTreeFormatter;
constexpr const char kMacAction[]{"mac/action"};
constexpr const char kMacAttributedString[]{"mac/attributed-string"};
constexpr const char kMacAttributes[]{"mac/attributes"};
constexpr const char kMacDescription[]{"mac/description"};
constexpr const char kMacSelection[]{"mac/selection"};
constexpr const char kMacTextMarker[]{"mac/textmarker"};
constexpr const char kMacMethods[]{"mac/methods"};
Expand Down Expand Up @@ -452,6 +457,15 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, ChromeAXNodeId) {
RunTypedTest<kMacAttributes>("chrome-ax-node-id.html");
}

// Before macOS 11 aria-description must be exposed in AXHelp, and since macOS
// 11, it should only be exposed in AXCustomContent.
IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AriaDescription) {
if (base::mac::IsAtLeastOS11())
RunTypedTest<kMacDescription>("aria-description-in-axcustomcontent.html");
else
RunTypedTest<kMacDescription>("aria-description-in-axhelp.html");
}

IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, SelectAllTextarea) {
RunTypedTest<kMacSelection>("selectall-textarea.html");
}
Expand Down
@@ -1,4 +1,4 @@
AXWebArea AXRoleDescription='HTML content'
++AXGroup AXSubrole=AXApplicationLog AXARIABusy=1 AXDescription='Busy log' AXRoleDescription='log'
++AXGroup AXSubrole=AXApplicationLog AXDescription='Not-busy log' AXRoleDescription='log'
++AXGroup AXARIABusy=1 AXHelp='plain div' AXRoleDescription='group'
++AXGroup AXARIABusy=1 AXRoleDescription='group'
1 change: 0 additions & 1 deletion content/test/data/accessibility/aria/aria-busy.html
@@ -1,7 +1,6 @@
<!--
@MAC-ALLOW:AXARIABusy
@MAC-ALLOW:AXRoleDescription
@MAC-ALLOW:AXHelp
@WIN-ALLOW:BUSY*
@WIN-DENY:description:*
@WIN-DENY:description-from*
Expand Down
@@ -1,5 +1,5 @@
AXWebArea
++AXGroup AXDescription='description' AXHelp='Text-description'
++AXGroup AXDescription='both' AXHelp='Description from describedby'
++AXGroup AXDescription='description'
++AXGroup AXDescription='both'
++AXGroup AXSubrole=AXUserInterfaceTooltip AXTitle='Description from describedby'
++++AXStaticText AXValue='Description from describedby'
1 change: 0 additions & 1 deletion content/test/data/accessibility/aria/aria-description.html
Expand Up @@ -3,7 +3,6 @@
@BLINK-ALLOW:desc*
@WIN-ALLOW:desc*
@AURALINUX-ALLOW:desc*
@MAC-ALLOW:AXHelp*
-->
<!--
Note, in the IA2/ATK output, the description property and object attribute are different.
Expand Down
@@ -1,12 +1,12 @@
AXWebArea
++AXGroup AXDetailsElements=[:10] AXHelp='comment'
++AXGroup AXDetailsElements=[:12] AXHelp='comment-group'
++AXGroup AXDetailsElements=[:18] AXHelp='comment-region'
++AXGroup AXDetailsElements=[:21] AXHelp='comment-section'
++AXGroup AXDetailsElements=[:24] AXHelp='definition'
++AXGroup AXDetailsElements=[:26] AXHelp='doc-endnote'
++AXGroup AXDetailsElements=[:28] AXHelp='doc-footnote'
++AXGroup AXDetailsElements=[:12, :10, :24, :28] AXHelp='many'
++AXGroup AXDetailsElements=[:10]
++AXGroup AXDetailsElements=[:12]
++AXGroup AXDetailsElements=[:18]
++AXGroup AXDetailsElements=[:21]
++AXGroup AXDetailsElements=[:24]
++AXGroup AXDetailsElements=[:26]
++AXGroup AXDetailsElements=[:28]
++AXGroup AXDetailsElements=[:12, :10, :24, :28]
++AXGroup
++++AXStaticText AXValue='x'
++AXGroup AXSubrole=AXApplicationGroup
Expand Down
@@ -1,6 +1,5 @@
<!--
@MAC-ALLOW:AXDetailsElements
@MAC-ALLOW:AXHelp
@WIN-ALLOW:details-roles:*
@WIN-DENY:description*
@WIN-ALLOW:description=*
Expand Down
Expand Up @@ -21,5 +21,5 @@ AXWebArea
++++++++++++AXGroup
++++++++++AXPopUpButton AXDescription='show more media controls' AXHelp='more options'
++++++++++++AXGroup
++++++++AXSlider AXDescription='video time scrubber' AXHelp='total time: 0:00' AXValue=0
++AXTextArea
++++++++AXSlider AXDescription='video time scrubber' AXValue=0
++AXTextArea
Expand Up @@ -12,6 +12,6 @@ AXWebArea
++++++++++++++AXButton AXDescription='mute'
++++++++++++AXButton AXDescription='enter full screen'
++++++++++++++AXGroup
++++++++++++AXPopUpButton AXDescription='show more media controls' AXHelp='more options'
++++++++++++AXPopUpButton AXDescription='show more media controls'
++++++++++++++AXGroup
++++++++++AXSlider AXDescription='video time scrubber' AXHelp='total time: 0:00' AXValue=0
++++++++++AXSlider AXDescription='video time scrubber' AXValue=0
1 change: 0 additions & 1 deletion content/test/data/accessibility/html/no-source-video.html
@@ -1,6 +1,5 @@
<!--
@WIN-ALLOW:description*
@MAC-ALLOW:AXHelp
-->
<!DOCTYPE html>
<html>
Expand Down
@@ -0,0 +1,4 @@
div.accessibilityCustomContent[0].label='description'
div.accessibilityCustomContent[0].value='Text-description'
div.accessibilityAttributeValue(AXHelp)=NULL
div_with_tooltip.accessibilityAttributeValue(AXHelp)='Description from describedby'
@@ -0,0 +1,18 @@
<!--
@SCRIPT:
div.accessibilityCustomContent[0].label
div.accessibilityCustomContent[0].value
div.accessibilityAttributeValue(AXHelp)
div_with_tooltip.accessibilityAttributeValue(AXHelp)
-->
<!--
Note, this test will only run when macOS is greater or equal to 11. The purpose of the test is to show that AXHelp DOES NOT surface the aria-description string beginning with macOS 11 (instead, it can be found in AXCustomContent).
-->
<!DOCTYPE html>
<html>
<body>
<div id="div" aria-label="description" aria-description="Text-description" ></div>
<div id="div_with_tooltip" aria-label="both" aria-description="describedby overrides description" aria-describedby="desc1" ></div>
<div role="tooltip" id="desc1">Description from describedby</div>
</body>
</html>
@@ -0,0 +1,2 @@
div.accessibilityAttributeValue(AXHelp)='Text-description'
div_with_tooltip.accessibilityAttributeValue(AXHelp)='Description from describedby'
@@ -0,0 +1,16 @@
<!--
@SCRIPT:
div.accessibilityAttributeValue(AXHelp)
div_with_tooltip.accessibilityAttributeValue(AXHelp)
-->
<!--
Note, this test will only run when macOS is less than 11, and the purpose of the test is to show that AXHelp DOES surface the aria-description string.
-->
<!DOCTYPE html>
<html>
<body>
<div id="div" aria-label="description" aria-description="Text-description" ></div>
<div id="div_with_tooltip" aria-label="both" aria-description="describedby overrides description" aria-describedby="desc1" ></div>
<div role="tooltip" id="desc1">Description from describedby</div>
</body>
</html>
3 changes: 3 additions & 0 deletions ui/accessibility/platform/BUILD.gn
Expand Up @@ -245,6 +245,9 @@ component("platform") {
"AppKit.framework",
"Foundation.framework",
]
weak_frameworks = [
"Accessibility.framework", # macOS 11
]
}

if (use_atk) {
Expand Down
6 changes: 5 additions & 1 deletion ui/accessibility/platform/ax_platform_node_cocoa.h
Expand Up @@ -7,6 +7,7 @@

#include <memory>

#import <Accessibility/Accessibility.h>
#import <Cocoa/Cocoa.h>

#include "base/component_export.h"
Expand All @@ -30,7 +31,10 @@ struct AXAnnouncementSpec {
} // namespace ui

COMPONENT_EXPORT(AX_PLATFORM)
@interface AXPlatformNodeCocoa : NSAccessibilityElement <NSAccessibility>
@interface AXPlatformNodeCocoa
: NSAccessibilityElement <NSAccessibility, AXCustomContentProvider>

- (NSArray*)accessibilityCustomContent;

// Determines if this object is alive, i.e. it hasn't been detached.
- (BOOL)instanceActive;
Expand Down
42 changes: 41 additions & 1 deletion ui/accessibility/platform/ax_platform_node_cocoa.mm
Expand Up @@ -7,8 +7,8 @@
#import <Cocoa/Cocoa.h>

#include "base/logging.h"

#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "base/no_destructor.h"
#include "base/strings/sys_string_conversions.h"
#include "base/trace_event/trace_event.h"
Expand Down Expand Up @@ -235,6 +235,8 @@ @implementation AXPlatformNodeCocoa {
}

@synthesize node = _node;
// Required for AXCustomContentProvider, which defines the property.
@synthesize accessibilityCustomContent = _accessibilityCustomContent;

- (ui::AXPlatformNodeDelegate*)nodeDelegate {
return _node ? _node->GetDelegate() : nil;
Expand Down Expand Up @@ -809,6 +811,17 @@ - (void)addTextAnnotationsIn:(const AXRange*)axRange
[attributedString endEditing];
}

- (NSString*)descriptionIfFromAriaDescription {
// TODO(crbug.com/1373178): Eventually this should include the DescriptionFrom
// for content from aria-describedby, kRelatedContent.
ax::mojom::DescriptionFrom descFrom = static_cast<ax::mojom::DescriptionFrom>(
_node->GetIntAttribute(ax::mojom::IntAttribute::kDescriptionFrom));
if (descFrom == ax::mojom::DescriptionFrom::kAriaDescription) {
return [self getStringAttribute:ax::mojom::StringAttribute::kDescription];
}
return nil;
}

- (NSString*)getName {
return base::SysUTF8ToNSString(_node->GetName());
}
Expand Down Expand Up @@ -1646,6 +1659,14 @@ - (NSString*)AXHelp {
if (![self instanceActive])
return nil;

// AXCustomContent is only supported by VoiceOver since macOS 11. In
// macOS 11 or later we expose the aria description in AXCustomContent,
// before then we expose the description in AXHelp.
if (base::mac::IsAtLeastOS11() &&
[[self descriptionIfFromAriaDescription] length]) {
return nil;
}

return [self getStringAttribute:ax::mojom::StringAttribute::kDescription];
}

Expand Down Expand Up @@ -1947,10 +1968,29 @@ - (BOOL)isAccessibilityEnabled {
return _node->GetData().GetRestriction() != ax::mojom::Restriction::kDisabled;
}

- (NSArray*)accessibilityCustomContent {
if (![self instanceActive])
return nil;

if (@available(macOS 11.0, *)) {
NSString* description = [self descriptionIfFromAriaDescription];
if ([description length]) {
return @[ [AXCustomContent customContentWithLabel:@"description"
value:description] ];
}
}

return nil;
}

- (NSRect)accessibilityFrame {
return [self boundsInScreen];
}

- (NSString*)accessibilityHelp {
return [self AXHelp];
}

- (NSString*)accessibilityLabel {
if (![self instanceActive])
return nil;
Expand Down
Expand Up @@ -41,6 +41,11 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXCallStatementInvoker final {
AXOptionalNSObject InvokeFor(const id target,
const AXPropertyNode& property_node) const;

// Invoke a property node for a given AXCustomContent.
AXOptionalNSObject InvokeForAXCustomContent(
const id target,
const AXPropertyNode& property_node) const;

// Invokes a property node for a given AXElement.
AXOptionalNSObject InvokeForAXElement(
const AXElementWrapper& ax_element,
Expand Down
25 changes: 25 additions & 0 deletions ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.mm
Expand Up @@ -4,6 +4,8 @@

#include "ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.h"

#import <Accessibility/Accessibility.h>

#include "base/strings/sys_string_conversions.h"
#include "ui/accessibility/platform/ax_utils_mac.h"
#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
Expand Down Expand Up @@ -167,10 +169,33 @@
if ([target isKindOfClass:[NSDictionary class]])
return InvokeForDictionary(target, property_node);

if (@available(macOS 11.0, *)) {
if ([target isKindOfClass:[AXCustomContent class]])
return InvokeForAXCustomContent(target, property_node);
}

LOG(ERROR) << "Unexpected target type for " << property_node.ToFlatString();
return AXOptionalNSObject::Error();
}

AXOptionalNSObject AXCallStatementInvoker::InvokeForAXCustomContent(
const id target,
const AXPropertyNode& property_node) const {
if (@available(macOS 11.0, *)) {
AXCustomContent* content = target;

if (property_node.name_or_value == "label")
return AXOptionalNSObject(content.label);
if (property_node.name_or_value == "value")
return AXOptionalNSObject(content.value);

return AXOptionalNSObject::Error(
"Unrecognized '" + property_node.name_or_value +
"' attribute called on AXCustomContent object.");
}
return AXOptionalNSObject::Error();
}

AXOptionalNSObject AXCallStatementInvoker::InvokeForAXElement(
const AXElementWrapper& ax_element,
const AXPropertyNode& property_node) const {
Expand Down
32 changes: 29 additions & 3 deletions ui/views/widget/ax_native_widget_mac_unittest.mm
Expand Up @@ -4,6 +4,7 @@

#include <memory>

#import <Accessibility/Accessibility.h>
#import <Cocoa/Cocoa.h>

#include "base/mac/mac_util.h"
Expand Down Expand Up @@ -329,14 +330,39 @@ void TearDown() override {
EXPECT_NSEQ(widget_origin, A11yElementAtMidpoint().accessibilityFrame.origin);
}

// Test for NSAccessibilityHelpAttribute.
TEST_F(AXNativeWidgetMacTest, HelpAttribute) {
// Test for surfacing information in TooltipText.
TEST_F(AXNativeWidgetMacTest, TooltipText) {
Label* label = new Label(base::SysNSStringToUTF16(kTestStringValue));
label->SetSize(GetWidgetBounds().size());
EXPECT_NSEQ(nil, A11yElementAtMidpoint().accessibilityHelp);
label->SetTooltipText(base::SysNSStringToUTF16(kTestPlaceholderText));
widget()->GetContentsView()->AddChildView(label);
EXPECT_NSEQ(kTestPlaceholderText, A11yElementAtMidpoint().accessibilityHelp);

// The tooltip is exposed in accessibilityHelp only before macOS 11. After,
// it is accessibilityCustomContent. This is because the DescriptionFrom
// for the ToolTip string has been been set to kAriaDescription, and
// `aria-description` is exposed in AXCustomContent.
id<NSAccessibility> element = A11yElementAtMidpoint();

if (@available(macOS 11.0, *)) {
NSString* description = nil;
ASSERT_TRUE(
[element conformsToProtocol:@protocol(AXCustomContentProvider)]);
auto element_with_content =
static_cast<id<AXCustomContentProvider>>(element);
for (AXCustomContent* content in element_with_content
.accessibilityCustomContent) {
if ([content.label isEqualToString:@"description"]) {
// There should be only one AXCustomContent with the label
// "description".
EXPECT_EQ(description, nil);
description = content.value;
}
}
EXPECT_NSEQ(kTestPlaceholderText, description);
} else {
EXPECT_NSEQ(kTestPlaceholderText, element.accessibilityHelp);
}
}

// Test view properties that should report the native NSWindow, and test
Expand Down

0 comments on commit 1c4e63e

Please sign in to comment.