Include checkbox in check state update (#35557)
* Include checkbox in check state update
* Windows test for checkbox native state
* Reformat to appease linux_unopt test
* More format hoops
* Update accessibility_bridge_unittests.cc
* Update flutter_windows_view_unittests.cc
diff --git a/shell/platform/common/accessibility_bridge.cc b/shell/platform/common/accessibility_bridge.cc
index ab2468e..576edfa 100644
--- a/shell/platform/common/accessibility_bridge.cc
+++ b/shell/platform/common/accessibility_bridge.cc
@@ -375,7 +375,8 @@
node_data.AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, sel_start);
node_data.AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, sel_end);
- if (node_data.role == ax::mojom::Role::kRadioButton) {
+ if (node_data.role == ax::mojom::Role::kRadioButton ||
+ node_data.role == ax::mojom::Role::kCheckBox) {
node_data.AddIntAttribute(
ax::mojom::IntAttribute::kCheckedState,
static_cast<int32_t>(
diff --git a/shell/platform/common/accessibility_bridge_unittests.cc b/shell/platform/common/accessibility_bridge_unittests.cc
index 38e0382..41834b1 100644
--- a/shell/platform/common/accessibility_bridge_unittests.cc
+++ b/shell/platform/common/accessibility_bridge_unittests.cc
@@ -337,5 +337,36 @@
EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSlider);
}
+// Ensure that checkboxes have their checked status set apropriately
+// Previously, only Radios could have this flag updated
+// Resulted in the issue seen at
+// https://github.com/flutter/flutter/issues/96218
+// As this fix involved code run on all platforms, it is included here.
+TEST(AccessibilityBridgeTest, CanSetCheckboxChecked) {
+ std::shared_ptr<AccessibilityBridge> bridge =
+ std::make_shared<AccessibilityBridge>(
+ std::make_unique<TestAccessibilityBridgeDelegate>());
+ FlutterSemanticsNode root;
+ root.id = 0;
+ root.label = "root";
+ root.hint = "";
+ root.value = "";
+ root.increased_value = "";
+ root.decreased_value = "";
+ root.child_count = 0;
+ root.custom_accessibility_actions_count = 0;
+ root.flags = static_cast<FlutterSemanticsFlag>(
+ FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
+ FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked);
+ bridge->AddFlutterSemanticsNodeUpdate(&root);
+
+ bridge->CommitUpdates();
+
+ auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
+ EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
+ EXPECT_EQ(root_node->GetData().GetCheckedState(),
+ ax::mojom::CheckedState::kTrue);
+}
+
} // namespace testing
} // namespace flutter
diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc
index 9513322..d6830d6 100644
--- a/shell/platform/windows/flutter_windows_view_unittests.cc
+++ b/shell/platform/windows/flutter_windows_view_unittests.cc
@@ -597,5 +597,95 @@
EXPECT_TRUE(schedule_frame_called);
}
+// Ensure that checkboxes have their checked status set apropriately
+// Previously, only Radios could have this flag updated
+// Resulted in the issue seen at
+// https://github.com/flutter/flutter/issues/96218
+// This test ensures that the native state of Checkboxes on Windows,
+// specifically, is updated as desired.
+TEST(FlutterWindowsViewTest, CheckboxNativeState) {
+ std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
+ EngineModifier modifier(engine.get());
+ modifier.embedder_api().UpdateSemanticsEnabled =
+ [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
+ return kSuccess;
+ };
+
+ auto window_binding_handler =
+ std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
+ FlutterWindowsView view(std::move(window_binding_handler));
+ view.SetEngine(std::move(engine));
+
+ // Enable semantics to instantiate accessibility bridge.
+ view.OnUpdateSemanticsEnabled(true);
+
+ auto bridge = view.GetEngine()->accessibility_bridge().lock();
+ ASSERT_TRUE(bridge);
+
+ FlutterSemanticsNode root{sizeof(FlutterSemanticsNode), 0};
+ root.id = 0;
+ root.label = "root";
+ root.hint = "";
+ root.value = "";
+ root.increased_value = "";
+ root.decreased_value = "";
+ root.child_count = 0;
+ root.custom_accessibility_actions_count = 0;
+ root.flags = static_cast<FlutterSemanticsFlag>(
+ FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
+ FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked);
+ bridge->AddFlutterSemanticsNodeUpdate(&root);
+
+ bridge->CommitUpdates();
+
+ auto root_node = bridge
+ ->GetFlutterPlatformNodeDelegateFromID(
+ AccessibilityBridge::kRootNodeId)
+ .lock();
+ EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
+ EXPECT_EQ(root_node->GetData().GetCheckedState(),
+ ax::mojom::CheckedState::kTrue);
+
+ // Get the IAccessible for the root node.
+ IAccessible* native_view = root_node->GetNativeViewAccessible();
+ ASSERT_TRUE(native_view != nullptr);
+
+ // Look up against the node itself (not one of its children)
+ VARIANT varchild = {};
+ varchild.vt = VT_I4;
+
+ // Verify the checkbox is checked.
+ varchild.lVal = CHILDID_SELF;
+ VARIANT native_state = {};
+ ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
+ EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_CHECKED);
+
+ // Test unchecked too
+ root.flags = static_cast<FlutterSemanticsFlag>(
+ FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState);
+ bridge->AddFlutterSemanticsNodeUpdate(&root);
+ bridge->CommitUpdates();
+ root_node = bridge
+ ->GetFlutterPlatformNodeDelegateFromID(
+ AccessibilityBridge::kRootNodeId)
+ .lock();
+ EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
+ EXPECT_EQ(root_node->GetData().GetCheckedState(),
+ ax::mojom::CheckedState::kFalse);
+
+ // Get the IAccessible for the root node.
+ native_view = root_node->GetNativeViewAccessible();
+
+ // Look up against the node itself (not one of its children)
+ varchild = {};
+ varchild.vt = VT_I4;
+
+ // Verify the checkbox is checked.
+ varchild.lVal = CHILDID_SELF;
+ native_state = {};
+ ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
+ EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_CHECKED);
+}
+
} // namespace testing
} // namespace flutter