diff --git a/shell/platform/common/accessibility_bridge.cc b/shell/platform/common/accessibility_bridge.cc index 1913a580c509e..0d7c66762c78b 100644 --- a/shell/platform/common/accessibility_bridge.cc +++ b/shell/platform/common/accessibility_bridge.cc @@ -291,6 +291,7 @@ void AccessibilityBridge::ConvertFlutterUpdate(const SemanticsNode& node, SetIntAttributesFromFlutterUpdate(node_data, node); SetIntListAttributesFromFlutterUpdate(node_data, node); SetStringListAttributesFromFlutterUpdate(node_data, node); + SetIdentifierFromFlutterUpdate(node_data, node); SetNameFromFlutterUpdate(node_data, node); SetValueFromFlutterUpdate(node_data, node); SetTooltipFromFlutterUpdate(node_data, node); @@ -530,6 +531,13 @@ void AccessibilityBridge::SetStringListAttributesFromFlutterUpdate( } } +void AccessibilityBridge::SetIdentifierFromFlutterUpdate( + ui::AXNodeData& node_data, + const SemanticsNode& node) { + node_data.AddStringAttribute(ax::mojom::StringAttribute::kAuthorUniqueId, + node.identifier); +} + void AccessibilityBridge::SetNameFromFlutterUpdate(ui::AXNodeData& node_data, const SemanticsNode& node) { node_data.SetName(node.label); @@ -598,6 +606,9 @@ AccessibilityBridge::FromFlutterSemanticsNode( result.scroll_extent_min = flutter_node.scroll_extent_min; result.elevation = flutter_node.elevation; result.thickness = flutter_node.thickness; + if (flutter_node.identifier) { + result.identifier = std::string(flutter_node.identifier); + } if (flutter_node.label) { result.label = std::string(flutter_node.label); } diff --git a/shell/platform/common/accessibility_bridge.h b/shell/platform/common/accessibility_bridge.h index 8126c1ce7b65a..9b49eea2bf30c 100644 --- a/shell/platform/common/accessibility_bridge.h +++ b/shell/platform/common/accessibility_bridge.h @@ -172,6 +172,7 @@ class AccessibilityBridge double scroll_extent_min; double elevation; double thickness; + std::string identifier; std::string label; std::string hint; std::string value; @@ -227,6 +228,8 @@ class AccessibilityBridge const SemanticsNode& node); void SetStringListAttributesFromFlutterUpdate(ui::AXNodeData& node_data, const SemanticsNode& node); + void SetIdentifierFromFlutterUpdate(ui::AXNodeData& node_data, + const SemanticsNode& node); void SetNameFromFlutterUpdate(ui::AXNodeData& node_data, const SemanticsNode& node); void SetValueFromFlutterUpdate(ui::AXNodeData& node_data, diff --git a/shell/platform/common/flutter_platform_node_delegate_unittests.cc b/shell/platform/common/flutter_platform_node_delegate_unittests.cc index 00e3240fa4f6c..b3b6e4c0359d5 100644 --- a/shell/platform/common/flutter_platform_node_delegate_unittests.cc +++ b/shell/platform/common/flutter_platform_node_delegate_unittests.cc @@ -46,6 +46,7 @@ TEST(FlutterPlatformNodeDelegateTest, canPerfomActions) { root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -91,6 +92,7 @@ TEST(FlutterPlatformNodeDelegateTest, canGetAXNode) { root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -112,6 +114,7 @@ TEST(FlutterPlatformNodeDelegateTest, canCalculateBoundsCorrectly) { std::make_shared(); FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -128,6 +131,7 @@ TEST(FlutterPlatformNodeDelegateTest, canCalculateBoundsCorrectly) { FlutterSemanticsNode2 child1; child1.id = 1; + child1.identifier = ""; child1.label = "child 1"; child1.hint = ""; child1.value = ""; @@ -158,6 +162,7 @@ TEST(FlutterPlatformNodeDelegateTest, canCalculateOffScreenBoundsCorrectly) { std::make_shared(); FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -174,6 +179,7 @@ TEST(FlutterPlatformNodeDelegateTest, canCalculateOffScreenBoundsCorrectly) { FlutterSemanticsNode2 child1; child1.id = 1; + child1.identifier = ""; child1.label = "child 1"; child1.hint = ""; child1.value = ""; @@ -204,6 +210,7 @@ TEST(FlutterPlatformNodeDelegateTest, canUseOwnerBridge) { std::make_shared(); FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -220,6 +227,7 @@ TEST(FlutterPlatformNodeDelegateTest, canUseOwnerBridge) { FlutterSemanticsNode2 child1; child1.id = 1; + child1.identifier = ""; child1.label = "child 1"; child1.hint = ""; child1.value = ""; @@ -236,7 +244,7 @@ TEST(FlutterPlatformNodeDelegateTest, canUseOwnerBridge) { auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock(); auto owner_bridge = child1_node->GetOwnerBridge().lock(); - bool result; + bool result = false; gfx::RectF bounds = owner_bridge->RelativeToGlobalBounds( child1_node->GetAXNode(), result, true); EXPECT_EQ(bounds.x(), 0); @@ -251,6 +259,7 @@ TEST(FlutterPlatformNodeDelegateTest, selfIsLowestPlatformAncestor) { std::make_shared(); FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -273,6 +282,7 @@ TEST(FlutterPlatformNodeDelegateTest, canGetFromNodeID) { std::make_shared(); FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -287,6 +297,7 @@ TEST(FlutterPlatformNodeDelegateTest, canGetFromNodeID) { FlutterSemanticsNode2 child1; child1.id = 1; + child1.identifier = ""; child1.label = "child 1"; child1.hint = ""; child1.value = ""; diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm index 12a83ec030dc9..f98a757bab7c3 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm @@ -104,6 +104,7 @@ @implementation AccessibilityBridgeTestViewController root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -163,6 +164,7 @@ @implementation AccessibilityBridgeTestViewController node1.actions = static_cast(0); node1.text_selection_base = -1; node1.text_selection_extent = -1; + node1.identifier = ""; node1.label = "node1"; node1.hint = ""; node1.value = ""; @@ -180,6 +182,7 @@ @implementation AccessibilityBridgeTestViewController node2.actions = static_cast(0); node2.text_selection_base = -1; node2.text_selection_extent = -1; + node2.identifier = ""; node2.label = "node2"; node2.hint = ""; node2.value = ""; @@ -225,6 +228,7 @@ @implementation AccessibilityBridgeTestViewController root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -271,6 +275,7 @@ @implementation AccessibilityBridgeTestViewController root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm index ceb4d1bdf0b04..296cc4a477c50 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm @@ -42,6 +42,7 @@ root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = "accessibility"; root.hint = ""; root.value = ""; @@ -79,6 +80,7 @@ root.actions = static_cast(0); root.text_selection_base = 1; root.text_selection_extent = 3; + root.identifier = ""; root.label = ""; root.hint = ""; // Selectable text store its text in value @@ -121,6 +123,7 @@ root.actions = static_cast(0); root.text_selection_base = -1; root.text_selection_extent = -1; + root.identifier = ""; root.label = ""; root.hint = ""; // Selectable text store its text in value @@ -162,6 +165,7 @@ // Initialize ax node data. FlutterSemanticsNode2 root; root.id = 0; + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -176,6 +180,7 @@ FlutterSemanticsNode2 child1; child1.id = 1; + child1.identifier = ""; child1.label = "child 1"; child1.hint = ""; child1.value = ""; @@ -239,6 +244,7 @@ root.id = 0; root.flags = static_cast(0); root.actions = static_cast(0); + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -260,6 +266,7 @@ child1.id = 1; child1.flags = FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField; child1.actions = static_cast(0); + child1.identifier = ""; child1.label = ""; child1.hint = ""; child1.value = "textfield"; @@ -315,6 +322,7 @@ root.id = 0; root.flags = static_cast(0); root.actions = static_cast(0); + root.identifier = ""; root.label = "root"; root.hint = ""; root.value = ""; @@ -336,6 +344,7 @@ child1.id = 1; child1.flags = static_cast(0); child1.actions = static_cast(0); + child1.identifier = ""; child1.label = ""; child1.hint = ""; child1.value = "textfield"; diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 9e9b875a2cc36..d4fa67197ce37 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1423,6 +1423,8 @@ typedef struct { double elevation; /// Describes how much space the semantics node takes up along the z-axis. double thickness; + /// A technical identifier for the node. + const char* identifier; /// A textual description of the node. const char* label; /// A brief description of the result of performing an action on the node. diff --git a/shell/platform/embedder/embedder_semantics_update.cc b/shell/platform/embedder/embedder_semantics_update.cc index 1670d7c7341e6..a47b679bf0194 100644 --- a/shell/platform/embedder/embedder_semantics_update.cc +++ b/shell/platform/embedder/embedder_semantics_update.cc @@ -153,6 +153,7 @@ void EmbedderSemanticsUpdate2::AddNode(const SemanticsNode& node) { node.scrollExtentMin, node.elevation, node.thickness, + node.identifier.c_str(), node.label.c_str(), node.hint.c_str(), node.value.c_str(), diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.cc b/shell/platform/windows/flutter_platform_node_delegate_windows.cc index 56d8547b5d21f..001038b9ec59b 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.cc +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.cc @@ -7,6 +7,7 @@ #include "flutter/shell/platform/windows/flutter_platform_node_delegate_windows.h" #include "flutter/fml/logging.h" +#include "flutter/fml/platform/win/wstring_conversion.h" #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/third_party/accessibility/ax/ax_clipping_behavior.h" @@ -78,6 +79,13 @@ gfx::NativeViewAccessible FlutterPlatformNodeDelegateWindows::HitTestSync( return ax_platform_node_->GetNativeViewAccessible(); } +// |ui::AXPlatformNodeDelegate| +std::u16string FlutterPlatformNodeDelegateWindows::GetAuthorUniqueId() const { + return fml::WideStringToUtf16( + fml::Utf8ToWideString(GetData().GetStringAttribute( + ax::mojom::StringAttribute::kAuthorUniqueId))); +} + // |FlutterPlatformNodeDelegate| gfx::Rect FlutterPlatformNodeDelegateWindows::GetBoundsRect( const ui::AXCoordinateSystem coordinate_system, diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.h b/shell/platform/windows/flutter_platform_node_delegate_windows.h index af40d6cc24240..f6f5aa92ee5b5 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.h +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.h @@ -34,6 +34,9 @@ class FlutterPlatformNodeDelegateWindows : public FlutterPlatformNodeDelegate { int screen_physical_pixel_x, int screen_physical_pixel_y) const override; + // |ui::AXPlatformNodeDelegate| + std::u16string GetAuthorUniqueId() const override; + // |FlutterPlatformNodeDelegate| gfx::Rect GetBoundsRect( const ui::AXCoordinateSystem coordinate_system, diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 82e6cd9c486e5..ed5ec0b9082d3 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -811,7 +811,6 @@ LRESULT FlutterWindow::OnGetObject(UINT const message, if (root_view) { CreateAxFragmentRoot(); if (is_uia_request) { -#ifdef FLUTTER_ENGINE_USE_UIA // Retrieve UIA object for the root view. Microsoft::WRL::ComPtr root; if (SUCCEEDED( @@ -824,7 +823,6 @@ LRESULT FlutterWindow::OnGetObject(UINT const message, } else { FML_LOG(ERROR) << "Failed to query AX fragment root."; } -#endif // FLUTTER_ENGINE_USE_UIA } else if (is_msaa_request) { // Create the accessibility root if it does not already exist. // Return the IAccessible for the root view. diff --git a/shell/platform/windows/window_unittests.cc b/shell/platform/windows/window_unittests.cc index 97b3f70c55127..d1a77cfeb3334 100644 --- a/shell/platform/windows/window_unittests.cc +++ b/shell/platform/windows/window_unittests.cc @@ -366,9 +366,7 @@ TEST(MockWindow, DISABLED_GetObjectUia) { bool uia_called = false; ON_CALL(window, OnGetObject) .WillByDefault(Invoke([&uia_called](UINT msg, WPARAM wpar, LPARAM lpar) { -#ifdef FLUTTER_ENGINE_USE_UIA uia_called = true; -#endif // FLUTTER_ENGINE_USE_UIA return static_cast(0); })); EXPECT_CALL(window, OnGetObject).Times(1); diff --git a/third_party/accessibility/ax/ax_enum_util.cc b/third_party/accessibility/ax/ax_enum_util.cc index daa93b61bbc3e..14c74610979ba 100644 --- a/third_party/accessibility/ax/ax_enum_util.cc +++ b/third_party/accessibility/ax/ax_enum_util.cc @@ -1413,6 +1413,8 @@ const char* ToString(ax::mojom::StringAttribute string_attribute) { return "accessKey"; case ax::mojom::StringAttribute::kAriaInvalidValue: return "ariaInvalidValue"; + case ax::mojom::StringAttribute::kAuthorUniqueId: + return "authorUniqueId"; case ax::mojom::StringAttribute::kAutoComplete: return "autoComplete"; case ax::mojom::StringAttribute::kChildTreeId: @@ -1473,6 +1475,8 @@ ax::mojom::StringAttribute ParseStringAttribute(const char* string_attribute) { return ax::mojom::StringAttribute::kAccessKey; if (0 == strcmp(string_attribute, "ariaInvalidValue")) return ax::mojom::StringAttribute::kAriaInvalidValue; + if (0 == strcmp(string_attribute, "authorUniqueId")) + return ax::mojom::StringAttribute::kAuthorUniqueId; if (0 == strcmp(string_attribute, "autoComplete")) return ax::mojom::StringAttribute::kAutoComplete; if (0 == strcmp(string_attribute, "childTreeId")) diff --git a/third_party/accessibility/ax/ax_enums.h b/third_party/accessibility/ax/ax_enums.h index 1a444cdeea54e..454c529cb1019 100644 --- a/third_party/accessibility/ax/ax_enums.h +++ b/third_party/accessibility/ax/ax_enums.h @@ -523,6 +523,7 @@ enum class StringAttribute { kAccessKey, // Only used when invalid_state == invalid_state_other. kAriaInvalidValue, + kAuthorUniqueId, kAutoComplete, kChildTreeId, kClassName, diff --git a/third_party/accessibility/ax/ax_node_data.cc b/third_party/accessibility/ax/ax_node_data.cc index f13be3d2e6ef9..f6b3ee181d021 100644 --- a/third_party/accessibility/ax/ax_node_data.cc +++ b/third_party/accessibility/ax/ax_node_data.cc @@ -1444,6 +1444,9 @@ std::string AXNodeData::ToString() const { case ax::mojom::StringAttribute::kAriaInvalidValue: result += " aria_invalid_value=" + value; break; + case ax::mojom::StringAttribute::kAuthorUniqueId: + result += " author_unique_id=" + value; + break; case ax::mojom::StringAttribute::kAutoComplete: result += " autocomplete=" + value; break;