Commit 53a16cf1 authored by Sharon Yang's avatar Sharon Yang Committed by Commit Bot

[fuchsia] Add support for a11y sliders

* Add sliders to role conversions
* Add increment and decrement actions
* Update data structures in FakeSemanticTree
* Add waits for node updates in FakeSemanticTree
* Add test only action/event pair in browsertests so waiting for actions
  is more reliable

Test: AccessibilityBridgeTest.Slider
Bug: fuchsia:56295, 1122806
Change-Id: Id0658ada5b15da1c128b69c1346aee1d39f2a6f7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2314758
Commit-Queue: Sharon Yang <yangsharon@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812919}
parent 492ea388
...@@ -148,7 +148,8 @@ void AccessibilityBridge::AccessibilityEventReceived( ...@@ -148,7 +148,8 @@ void AccessibilityBridge::AccessibilityEventReceived(
// Run the pending callback with the hit. // Run the pending callback with the hit.
pending_hit_test_callbacks_[event.action_request_id](std::move(hit)); pending_hit_test_callbacks_[event.action_request_id](std::move(hit));
pending_hit_test_callbacks_.erase(event.action_request_id); pending_hit_test_callbacks_.erase(event.action_request_id);
} else if (event_received_callback_for_test_) { } else if (event_received_callback_for_test_ &&
event.event_type == ax::mojom::Event::kEndOfTest) {
std::move(event_received_callback_for_test_).Run(); std::move(event_received_callback_for_test_).Run();
} }
} }
...@@ -185,6 +186,13 @@ void AccessibilityBridge::OnAccessibilityActionRequested( ...@@ -185,6 +186,13 @@ void AccessibilityBridge::OnAccessibilityActionRequested(
web_contents_->GetMainFrame()->AccessibilityPerformAction(action_data); web_contents_->GetMainFrame()->AccessibilityPerformAction(action_data);
callback(true); callback(true);
if (event_received_callback_for_test_) {
// Perform an action with a corresponding event to signal the action has
// been pumped through.
action_data.action = ax::mojom::Action::kSignalEndOfTest;
web_contents_->GetMainFrame()->AccessibilityPerformAction(action_data);
}
} }
void AccessibilityBridge::HitTest(fuchsia::math::PointF local_point, void AccessibilityBridge::HitTest(fuchsia::math::PointF local_point,
......
...@@ -33,6 +33,8 @@ const char kParagraphName[] = "a third paragraph"; ...@@ -33,6 +33,8 @@ const char kParagraphName[] = "a third paragraph";
const char kOffscreenNodeName[] = "offscreen node"; const char kOffscreenNodeName[] = "offscreen node";
const size_t kPage1NodeCount = 9; const size_t kPage1NodeCount = 9;
const size_t kPage2NodeCount = 190; const size_t kPage2NodeCount = 190;
const size_t kInitialRangeValue = 51;
const size_t kStepSize = 3;
fuchsia::math::PointF GetCenterOfBox(fuchsia::ui::gfx::BoundingBox box) { fuchsia::math::PointF GetCenterOfBox(fuchsia::ui::gfx::BoundingBox box) {
fuchsia::math::PointF center; fuchsia::math::PointF center;
...@@ -307,3 +309,37 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) { ...@@ -307,3 +309,37 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
EXPECT_FALSE(is_offscreen); EXPECT_FALSE(is_offscreen);
} }
IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Slider) {
GURL page_url(embedded_test_server()->GetURL(kPage1Path));
ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
page_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
fuchsia::accessibility::semantics::Node* node =
semantics_manager_.semantic_tree()->GetNodeFromRole(
fuchsia::accessibility::semantics::Role::SLIDER);
EXPECT_TRUE(node);
EXPECT_TRUE(node->has_states() && node->states().has_range_value());
EXPECT_EQ(node->states().range_value(), kInitialRangeValue);
AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
base::RunLoop run_loop;
bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
semantics_manager_.RequestAccessibilityAction(
node->node_id(), fuchsia::accessibility::semantics::Action::INCREMENT);
semantics_manager_.RunUntilNumActionsHandledEquals(1);
run_loop.Run();
// Wait for the slider node to be updated, then check the value.
base::RunLoop run_loop2;
semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
node->node_id(), run_loop2.QuitClosure());
run_loop2.Run();
node = semantics_manager_.semantic_tree()->GetNodeWithId(node->node_id());
EXPECT_TRUE(node->has_states() && node->states().has_range_value());
EXPECT_EQ(node->states().range_value(), kInitialRangeValue + kStepSize);
}
...@@ -26,6 +26,8 @@ fuchsia::accessibility::semantics::Role ConvertRole(ax::mojom::Role role) { ...@@ -26,6 +26,8 @@ fuchsia::accessibility::semantics::Role ConvertRole(ax::mojom::Role role) {
return fuchsia::accessibility::semantics::Role::HEADER; return fuchsia::accessibility::semantics::Role::HEADER;
if (role == ax::mojom::Role::kImage) if (role == ax::mojom::Role::kImage)
return fuchsia::accessibility::semantics::Role::IMAGE; return fuchsia::accessibility::semantics::Role::IMAGE;
if (role == ax::mojom::Role::kSlider)
return fuchsia::accessibility::semantics::Role::SLIDER;
if (role == ax::mojom::Role::kTextField) if (role == ax::mojom::Role::kTextField)
return fuchsia::accessibility::semantics::Role::TEXT_FIELD; return fuchsia::accessibility::semantics::Role::TEXT_FIELD;
...@@ -47,6 +49,23 @@ fuchsia::accessibility::semantics::Attributes ConvertAttributes( ...@@ -47,6 +49,23 @@ fuchsia::accessibility::semantics::Attributes ConvertAttributes(
attributes.set_secondary_label(description.substr(0, MAX_LABEL_SIZE)); attributes.set_secondary_label(description.substr(0, MAX_LABEL_SIZE));
} }
if (node.IsRangeValueSupported()) {
fuchsia::accessibility::semantics::RangeAttributes range_attributes;
if (node.HasFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange)) {
range_attributes.set_min_value(
node.GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange));
}
if (node.HasFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange)) {
range_attributes.set_max_value(
node.GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange));
}
if (node.HasFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange)) {
range_attributes.set_step_delta(node.GetFloatAttribute(
ax::mojom::FloatAttribute::kStepValueForRange));
}
attributes.set_range(std::move(range_attributes));
}
return attributes; return attributes;
} }
...@@ -96,6 +115,12 @@ fuchsia::accessibility::semantics::States ConvertStates( ...@@ -96,6 +115,12 @@ fuchsia::accessibility::semantics::States ConvertStates(
states.set_value(value.substr(0, MAX_LABEL_SIZE)); states.set_value(value.substr(0, MAX_LABEL_SIZE));
} }
// The value a range element currently has.
if (node.HasFloatAttribute(ax::mojom::FloatAttribute::kValueForRange)) {
states.set_range_value(
node.GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange));
}
return states; return states;
} }
...@@ -178,6 +203,12 @@ bool ConvertAction(fuchsia::accessibility::semantics::Action fuchsia_action, ...@@ -178,6 +203,12 @@ bool ConvertAction(fuchsia::accessibility::semantics::Action fuchsia_action,
case fuchsia::accessibility::semantics::Action::DEFAULT: case fuchsia::accessibility::semantics::Action::DEFAULT:
*mojom_action = ax::mojom::Action::kDoDefault; *mojom_action = ax::mojom::Action::kDoDefault;
return true; return true;
case fuchsia::accessibility::semantics::Action::DECREMENT:
*mojom_action = ax::mojom::Action::kDecrement;
return true;
case fuchsia::accessibility::semantics::Action::INCREMENT:
*mojom_action = ax::mojom::Action::kIncrement;
return true;
case fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN: case fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN:
*mojom_action = ax::mojom::Action::kScrollToMakeVisible; *mojom_action = ax::mojom::Action::kScrollToMakeVisible;
return true; return true;
......
...@@ -35,6 +35,7 @@ bool FakeSemanticTree::IsTreeValid( ...@@ -35,6 +35,7 @@ bool FakeSemanticTree::IsTreeValid(
fuchsia::accessibility::semantics::Node* child = GetNodeWithId(c); fuchsia::accessibility::semantics::Node* child = GetNodeWithId(c);
if (!child) if (!child)
return false; return false;
is_valid &= IsTreeValid(child, tree_size); is_valid &= IsTreeValid(child, tree_size);
} }
return is_valid; return is_valid;
...@@ -60,29 +61,33 @@ void FakeSemanticTree::RunUntilNodeCountAtLeast(size_t count) { ...@@ -60,29 +61,33 @@ void FakeSemanticTree::RunUntilNodeCountAtLeast(size_t count) {
run_loop.Run(); run_loop.Run();
} }
void FakeSemanticTree::SetNodeUpdatedCallback(
uint32_t node_id,
base::OnceClosure node_updated_callback) {
node_wait_id_ = node_id;
on_node_updated_callback_ = std::move(node_updated_callback);
}
fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeWithId( fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeWithId(
uint32_t id) { uint32_t id) {
for (auto& node : nodes_) { auto it = nodes_.find(id);
if (node.has_node_id() && node.node_id() == id) { return it == nodes_.end() ? nullptr : &it->second;
return &node;
}
}
return nullptr;
} }
fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeFromLabel( fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeFromLabel(
base::StringPiece label) { base::StringPiece label) {
fuchsia::accessibility::semantics::Node* to_return = nullptr; fuchsia::accessibility::semantics::Node* to_return = nullptr;
for (auto& node : nodes_) { for (auto& n : nodes_) {
if (node.has_attributes() && node.attributes().has_label() && auto* node = &n.second;
node.attributes().label() == label) { if (node->has_attributes() && node->attributes().has_label() &&
node->attributes().label() == label) {
// There are sometimes multiple semantic nodes with the same label. Hit // There are sometimes multiple semantic nodes with the same label. Hit
// testing should return the node with the smallest node ID so behaviour // testing should return the node with the smallest node ID so behaviour
// is consistent with the hit testing API being called. // is consistent with the hit testing API being called.
if (!to_return) { if (!to_return) {
to_return = &node; to_return = node;
} else if (node.node_id() < to_return->node_id()) { } else if (node->node_id() < to_return->node_id()) {
to_return = &node; to_return = node;
} }
} }
} }
...@@ -90,24 +95,34 @@ fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeFromLabel( ...@@ -90,24 +95,34 @@ fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeFromLabel(
return to_return; return to_return;
} }
fuchsia::accessibility::semantics::Node* FakeSemanticTree::GetNodeFromRole(
fuchsia::accessibility::semantics::Role role) {
for (auto& n : nodes_) {
auto* node = &n.second;
if (node->has_role() && node->role() == role)
return node;
}
return nullptr;
}
void FakeSemanticTree::UpdateSemanticNodes( void FakeSemanticTree::UpdateSemanticNodes(
std::vector<fuchsia::accessibility::semantics::Node> nodes) { std::vector<fuchsia::accessibility::semantics::Node> nodes) {
nodes_.reserve(nodes.size() + nodes_.size()); bool wait_node_updated = false;
for (auto& node : nodes) { for (auto& node : nodes) {
// Delete an existing node that's being updated to avoid having duplicate if (node.node_id() == node_wait_id_ && on_node_updated_callback_)
// nodes. wait_node_updated = true;
DeleteSemanticNodes({node.node_id()});
nodes_.push_back(std::move(node)); nodes_[node.node_id()] = std::move(node);
} }
if (wait_node_updated)
std::move(on_node_updated_callback_).Run();
} }
void FakeSemanticTree::DeleteSemanticNodes(std::vector<uint32_t> node_ids) { void FakeSemanticTree::DeleteSemanticNodes(std::vector<uint32_t> node_ids) {
for (auto id : node_ids) { for (auto id : node_ids)
for (uint i = 0; i < nodes_.size(); i++) { nodes_.erase(id);
if (nodes_.at(i).node_id() == id)
nodes_.erase(nodes_.begin() + i);
}
}
} }
void FakeSemanticTree::CommitUpdates(CommitUpdatesCallback callback) { void FakeSemanticTree::CommitUpdates(CommitUpdatesCallback callback) {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <fuchsia/accessibility/semantics/cpp/fidl.h> #include <fuchsia/accessibility/semantics/cpp/fidl.h>
#include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h> #include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h> #include <lib/fidl/cpp/binding.h>
#include <unordered_map>
#include "base/callback.h" #include "base/callback.h"
...@@ -35,9 +36,16 @@ class FakeSemanticTree ...@@ -35,9 +36,16 @@ class FakeSemanticTree
void Disconnect(); void Disconnect();
void RunUntilNodeCountAtLeast(size_t count); void RunUntilNodeCountAtLeast(size_t count);
void SetNodeUpdatedCallback(uint32_t node_id,
base::OnceClosure node_updated_callback);
fuchsia::accessibility::semantics::Node* GetNodeWithId(uint32_t id); fuchsia::accessibility::semantics::Node* GetNodeWithId(uint32_t id);
// For both functions below, it is possible there are multiple nodes with the
// same identifier.
fuchsia::accessibility::semantics::Node* GetNodeFromLabel( fuchsia::accessibility::semantics::Node* GetNodeFromLabel(
base::StringPiece label); base::StringPiece label);
fuchsia::accessibility::semantics::Node* GetNodeFromRole(
fuchsia::accessibility::semantics::Role role);
// fuchsia::accessibility::semantics::SemanticTree implementation. // fuchsia::accessibility::semantics::SemanticTree implementation.
void UpdateSemanticNodes( void UpdateSemanticNodes(
...@@ -50,8 +58,11 @@ class FakeSemanticTree ...@@ -50,8 +58,11 @@ class FakeSemanticTree
private: private:
fidl::Binding<fuchsia::accessibility::semantics::SemanticTree> fidl::Binding<fuchsia::accessibility::semantics::SemanticTree>
semantic_tree_binding_; semantic_tree_binding_;
std::vector<fuchsia::accessibility::semantics::Node> nodes_; std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Node> nodes_;
base::RepeatingClosure on_commit_updates_; base::RepeatingClosure on_commit_updates_;
uint32_t node_wait_id_;
base::OnceClosure on_node_updated_callback_;
}; };
#endif // FUCHSIA_ENGINE_BROWSER_FAKE_SEMANTIC_TREE_H_ #endif // FUCHSIA_ENGINE_BROWSER_FAKE_SEMANTIC_TREE_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<p>a third paragraph</p> <p>a third paragraph</p>
<button>another button</button> <button>another button</button>
<button>button 3</button> <button>button 3</button>
<input type="range" min="0" max="100" value="51" step="3" class="slider" id="myRange">
<div style='height:1000px; width:1000px;'></div> <div style='height:1000px; width:1000px;'></div>
<p>offscreen node</p> <p>offscreen node</p>
<button>button 4</button> <button>button 4</button>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment