diff --git a/shell/platform/fuchsia/flutter/accessibility_bridge.cc b/shell/platform/fuchsia/flutter/accessibility_bridge.cc index c75edc6e8a58949947806c1afb119dc3050cc09c..dc50564590f375d4e3c966154b266e21e3110647 100644 --- a/shell/platform/fuchsia/flutter/accessibility_bridge.cc +++ b/shell/platform/fuchsia/flutter/accessibility_bridge.cc @@ -80,11 +80,38 @@ AccessibilityBridge::GetNodeAttributes(const flutter::SemanticsNode& node, } fuchsia::accessibility::semantics::States AccessibilityBridge::GetNodeStates( - const flutter::SemanticsNode& node) const { + const flutter::SemanticsNode& node, + size_t* additional_size) const { fuchsia::accessibility::semantics::States states; - if (node.HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { - states.set_checked(node.HasFlag(flutter::SemanticsFlags::kIsChecked)); + (*additional_size) += sizeof(fuchsia::accessibility::semantics::States); + + // Set checked state. + if (!node.HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { + states.set_checked_state( + fuchsia::accessibility::semantics::CheckedState::NONE); + } else { + states.set_checked_state( + node.HasFlag(flutter::SemanticsFlags::kIsChecked) + ? fuchsia::accessibility::semantics::CheckedState::CHECKED + : fuchsia::accessibility::semantics::CheckedState::UNCHECKED); } + + // Set selected state. + states.set_selected(node.HasFlag(flutter::SemanticsFlags::kIsSelected)); + + // Set hidden state. + states.set_hidden(node.HasFlag(flutter::SemanticsFlags::kIsHidden)); + + // Set value. + if (node.value.size() > fuchsia::accessibility::semantics::MAX_VALUE_SIZE) { + states.set_value(node.value.substr( + 0, fuchsia::accessibility::semantics::MAX_VALUE_SIZE)); + (*additional_size) += fuchsia::accessibility::semantics::MAX_VALUE_SIZE; + } else { + states.set_value(node.value); + (*additional_size) += node.value.size(); + } + return states; } @@ -199,7 +226,7 @@ void AccessibilityBridge::AddSemanticsNodeUpdate( .set_location(GetNodeLocation(flutter_node)) .set_transform(GetNodeTransform(flutter_node)) .set_attributes(GetNodeAttributes(flutter_node, &this_node_size)) - .set_states(GetNodeStates(flutter_node)) + .set_states(GetNodeStates(flutter_node, &this_node_size)) .set_child_ids(child_ids); this_node_size += kNodeIdSize * flutter_node.childrenInTraversalOrder.size(); diff --git a/shell/platform/fuchsia/flutter/accessibility_bridge.h b/shell/platform/fuchsia/flutter/accessibility_bridge.h index 4072c9696ecfe881c0a5ab619bf379a8fcedd50a..ea4e5c884e9c1acfdf48c09276b9b0935959df7e 100644 --- a/shell/platform/fuchsia/flutter/accessibility_bridge.h +++ b/shell/platform/fuchsia/flutter/accessibility_bridge.h @@ -133,7 +133,8 @@ class AccessibilityBridge // Derives the states for a Fuchsia semantics node from a Flutter semantics // node. fuchsia::accessibility::semantics::States GetNodeStates( - const flutter::SemanticsNode& node) const; + const flutter::SemanticsNode& node, + size_t* additional_size) const; // Gets the set of reachable descendants from the given node id. std::unordered_set GetDescendants(int32_t node_id) const; diff --git a/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc b/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc index ef2d619271661dff3bb8ae763b07667e910956a5..479246df6f0d55a95e3f32be2a2e4220889730e2 100644 --- a/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc +++ b/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc @@ -130,6 +130,106 @@ TEST_F(AccessibilityBridgeTest, DeletesChildrenTransitively) { EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); } +TEST_F(AccessibilityBridgeTest, PopulatesCheckedState) { + flutter::SemanticsNode node0; + node0.id = 0; + // HasCheckedState = true + // IsChecked = true + // IsSelected = false + // IsHidden = false + node0.flags |= static_cast(flutter::SemanticsFlags::kHasCheckedState); + node0.flags |= static_cast(flutter::SemanticsFlags::kIsChecked); + node0.value = "value"; + + accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}); + RunLoopUntilIdle(); + + EXPECT_EQ(0, semantics_manager_.DeleteCount()); + EXPECT_EQ(1, semantics_manager_.UpdateCount()); + EXPECT_EQ(1, semantics_manager_.CommitCount()); + EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size()); + const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u); + EXPECT_EQ(fuchsia_node.node_id(), static_cast(node0.id)); + EXPECT_TRUE(fuchsia_node.has_states()); + const auto& states = fuchsia_node.states(); + EXPECT_TRUE(states.has_checked_state()); + EXPECT_EQ(states.checked_state(), + fuchsia::accessibility::semantics::CheckedState::CHECKED); + EXPECT_TRUE(states.has_selected()); + EXPECT_FALSE(states.selected()); + EXPECT_TRUE(states.has_hidden()); + EXPECT_FALSE(states.hidden()); + EXPECT_TRUE(states.has_value()); + EXPECT_EQ(states.value(), node0.value); + + EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); + EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); +} + +TEST_F(AccessibilityBridgeTest, PopulatesSelectedState) { + flutter::SemanticsNode node0; + node0.id = 0; + // HasCheckedState = false + // IsChecked = false + // IsSelected = true + // IsHidden = false + node0.flags = static_cast(flutter::SemanticsFlags::kIsSelected); + + accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}); + RunLoopUntilIdle(); + + EXPECT_EQ(0, semantics_manager_.DeleteCount()); + EXPECT_EQ(1, semantics_manager_.UpdateCount()); + EXPECT_EQ(1, semantics_manager_.CommitCount()); + EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size()); + const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u); + EXPECT_EQ(fuchsia_node.node_id(), static_cast(node0.id)); + EXPECT_TRUE(fuchsia_node.has_states()); + const auto& states = fuchsia_node.states(); + EXPECT_TRUE(states.has_checked_state()); + EXPECT_EQ(states.checked_state(), + fuchsia::accessibility::semantics::CheckedState::NONE); + EXPECT_TRUE(states.has_selected()); + EXPECT_TRUE(states.selected()); + EXPECT_TRUE(states.has_hidden()); + EXPECT_FALSE(states.hidden()); + + EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); + EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); +} + +TEST_F(AccessibilityBridgeTest, PopulatesHiddenState) { + flutter::SemanticsNode node0; + node0.id = 0; + // HasCheckedState = false + // IsChecked = false + // IsSelected = false + // IsHidden = true + node0.flags = static_cast(flutter::SemanticsFlags::kIsHidden); + + accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}); + RunLoopUntilIdle(); + + EXPECT_EQ(0, semantics_manager_.DeleteCount()); + EXPECT_EQ(1, semantics_manager_.UpdateCount()); + EXPECT_EQ(1, semantics_manager_.CommitCount()); + EXPECT_EQ(1u, semantics_manager_.LastUpdatedNodes().size()); + const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u); + EXPECT_EQ(fuchsia_node.node_id(), static_cast(node0.id)); + EXPECT_TRUE(fuchsia_node.has_states()); + const auto& states = fuchsia_node.states(); + EXPECT_TRUE(states.has_checked_state()); + EXPECT_EQ(states.checked_state(), + fuchsia::accessibility::semantics::CheckedState::NONE); + EXPECT_TRUE(states.has_selected()); + EXPECT_FALSE(states.selected()); + EXPECT_TRUE(states.has_hidden()); + EXPECT_TRUE(states.hidden()); + + EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); + EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); +} + TEST_F(AccessibilityBridgeTest, TruncatesLargeLabel) { // Test that labels which are too long are truncated. flutter::SemanticsNode node0; @@ -174,6 +274,48 @@ TEST_F(AccessibilityBridgeTest, TruncatesLargeLabel) { EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); } +TEST_F(AccessibilityBridgeTest, TruncatesLargeValue) { + // Test that values which are too long are truncated. + flutter::SemanticsNode node0; + node0.id = 0; + + flutter::SemanticsNode node1; + node1.id = 1; + + flutter::SemanticsNode bad_node; + bad_node.id = 2; + bad_node.value = + std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE + 1, '2'); + + node0.childrenInTraversalOrder = {1, 2}; + + accessibility_bridge_->AddSemanticsNodeUpdate({ + {0, node0}, + {1, node1}, + {2, bad_node}, + }); + RunLoopUntilIdle(); + + EXPECT_EQ(0, semantics_manager_.DeleteCount()); + EXPECT_EQ(1, semantics_manager_.UpdateCount()); + EXPECT_EQ(1, semantics_manager_.CommitCount()); + EXPECT_EQ(3U, semantics_manager_.LastUpdatedNodes().size()); + auto trimmed_node = + std::find_if(semantics_manager_.LastUpdatedNodes().begin(), + semantics_manager_.LastUpdatedNodes().end(), + [id = static_cast(bad_node.id)]( + fuchsia::accessibility::semantics::Node const& node) { + return node.node_id() == id; + }); + ASSERT_NE(trimmed_node, semantics_manager_.LastUpdatedNodes().end()); + ASSERT_TRUE(trimmed_node->has_states()); + EXPECT_EQ( + trimmed_node->states().value(), + std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE, '2')); + EXPECT_FALSE(semantics_manager_.DeleteOverflowed()); + EXPECT_FALSE(semantics_manager_.UpdateOverflowed()); +} + TEST_F(AccessibilityBridgeTest, SplitsLargeUpdates) { // Test that labels which are too long are truncated. flutter::SemanticsNode node0; @@ -194,8 +336,8 @@ TEST_F(AccessibilityBridgeTest, SplitsLargeUpdates) { flutter::SemanticsNode node4; node4.id = 4; - node4.label = - std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, '4'); + node4.value = + std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE, '4'); node0.childrenInTraversalOrder = {1, 2}; node0.childrenInHitTestOrder = {1, 2};