Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,80 @@
[engine shutDownEngine];
}

TEST(FlutterPlatformNodeDelegateMac, SelectableTextHasCorrectSemantics) {
FlutterEngine* engine = CreateTestEngine();
engine.semanticsEnabled = YES;
auto bridge = engine.accessibilityBridge.lock();
// Initialize ax node data.
FlutterSemanticsNode root;
root.id = 0;
root.flags =
static_cast<FlutterSemanticsFlag>(FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField |
FlutterSemanticsFlag::kFlutterSemanticsFlagIsReadOnly);
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = 1;
root.text_selection_extent = 3;
root.label = "";
root.hint = "";
// Selectable text store its text in value
root.value = "selectable text";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 0;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);

bridge->CommitUpdates();

auto root_platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
// Verify the accessibility attribute matches.
NSAccessibilityElement* native_accessibility =
root_platform_node_delegate->GetNativeViewAccessible();
std::string value = [native_accessibility.accessibilityValue UTF8String];
EXPECT_EQ(value, "selectable text");
EXPECT_EQ(native_accessibility.accessibilityRole, NSAccessibilityStaticTextRole);
EXPECT_EQ([native_accessibility.accessibilityChildren count], 0u);
NSRange selection = native_accessibility.accessibilitySelectedTextRange;
EXPECT_EQ(selection.location, 1u);
EXPECT_EQ(selection.length, 2u);
std::string selected_text = [native_accessibility.accessibilitySelectedText UTF8String];
EXPECT_EQ(selected_text, "el");
}

TEST(FlutterPlatformNodeDelegateMac, SelectableTextWithoutSelectionReturnZeroRange) {
FlutterEngine* engine = CreateTestEngine();
engine.semanticsEnabled = YES;
auto bridge = engine.accessibilityBridge.lock();
// Initialize ax node data.
FlutterSemanticsNode root;
root.id = 0;
root.flags =
static_cast<FlutterSemanticsFlag>(FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField |
FlutterSemanticsFlag::kFlutterSemanticsFlagIsReadOnly);
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = -1;
root.text_selection_extent = -1;
root.label = "";
root.hint = "";
// Selectable text store its text in value
root.value = "selectable text";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 0;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);

bridge->CommitUpdates();

auto root_platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
// Verify the accessibility attribute matches.
NSAccessibilityElement* native_accessibility =
root_platform_node_delegate->GetNativeViewAccessible();
NSRange selection = native_accessibility.accessibilitySelectedTextRange;
EXPECT_TRUE(selection.location == NSNotFound);
EXPECT_EQ(selection.length, 0u);
}

TEST(FlutterPlatformNodeDelegateMac, CanPerformAction) {
FlutterEngine* engine = CreateTestEngine();
engine.semanticsEnabled = YES;
Expand Down
18 changes: 17 additions & 1 deletion third_party/accessibility/ax/platform/ax_platform_node_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,19 @@ - (id)AXValueInternal {
if (role == ax::mojom::Role::kTab)
return [self AXSelectedInternal];

if (ui::IsNameExposedInAXValueForRole(role))
if (ui::IsNameExposedInAXValueForRole(role)) {
if (role == ax::mojom::Role::kStaticText) {
// Static texts may store their texts in the value attributes. For
// example, the selectable text stores its text in value instead of
// name.
NSString* value = [self getName];
if (value.length == 0) {
value = [self getStringAttribute:ax::mojom::StringAttribute::kValue];
}
return value;
}
return [self getName];
}

if (_node->IsPlatformCheckable()) {
// Mixed checkbox state not currently supported in views, but could be.
Expand Down Expand Up @@ -699,6 +710,11 @@ - (NSValue*)AXSelectedTextRangeInternal {
start = _node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart);
end = _node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd);
}
NSAssert((start >= 0 && end >= 0) || (start == -1 && end == -1), @"selection is invalid");

if (start == -1 && end == -1) {
return [NSValue valueWithRange:{NSNotFound, 0}];
}

// NSRange cannot represent the direction the text was selected in.
return [NSValue valueWithRange:{static_cast<NSUInteger>(std::min(start, end)),
Expand Down