Skip to content

Commit

Permalink
Make AccessibilityBridge a AXPlatformTreeManager (flutter#38610)
Browse files Browse the repository at this point in the history
* Make Accessibility Bridge a tree manager

* Add FlutterPlatformNodeDelegate::GetPlatformNode

* Add unit test

* Formatting

* PR
  • Loading branch information
yaakovschectman authored Jan 3, 2023
1 parent 1d2ba73 commit c0b3f8f
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 16 deletions.
89 changes: 74 additions & 15 deletions shell/platform/common/accessibility_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <functional>
#include <utility>

#include "flutter/third_party/accessibility/ax/ax_tree_manager_map.h"
#include "flutter/third_party/accessibility/ax/ax_tree_update.h"
#include "flutter/third_party/accessibility/base/logging.h"

Expand All @@ -19,14 +20,20 @@ constexpr int kHasScrollingAction =
FlutterSemanticsAction::kFlutterSemanticsActionScrollDown;

// AccessibilityBridge
AccessibilityBridge::AccessibilityBridge() {
event_generator_.SetTree(&tree_);
tree_.AddObserver(static_cast<ui::AXTreeObserver*>(this));
AccessibilityBridge::AccessibilityBridge()
: tree_(std::make_unique<ui::AXTree>()) {
event_generator_.SetTree(tree_.get());
tree_->AddObserver(static_cast<ui::AXTreeObserver*>(this));
ui::AXTreeData data = tree_->data();
data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
tree_->UpdateData(data);
ui::AXTreeManagerMap::GetInstance().AddTreeManager(tree_->GetAXTreeID(),
this);
}

AccessibilityBridge::~AccessibilityBridge() {
event_generator_.ReleaseTree();
tree_.RemoveObserver(static_cast<ui::AXTreeObserver*>(this));
tree_->RemoveObserver(static_cast<ui::AXTreeObserver*>(this));
}

void AccessibilityBridge::AddFlutterSemanticsNodeUpdate(
Expand All @@ -51,9 +58,9 @@ void AccessibilityBridge::CommitUpdates() {
std::optional<ui::AXTreeUpdate> remove_reparented =
CreateRemoveReparentedNodesUpdate();
if (remove_reparented.has_value()) {
tree_.Unserialize(remove_reparented.value());
tree_->Unserialize(remove_reparented.value());

std::string error = tree_.error();
std::string error = tree_->error();
if (!error.empty()) {
FML_LOG(ERROR) << "Failed to update ui::AXTree, error: " << error;
assert(false);
Expand All @@ -63,7 +70,7 @@ void AccessibilityBridge::CommitUpdates() {

// Second, apply the pending node updates. This also moves reparented nodes to
// their new parents if needed.
ui::AXTreeUpdate update{.tree_data = tree_.data()};
ui::AXTreeUpdate update{.tree_data = tree_->data()};

// Figure out update order, ui::AXTree only accepts update in tree order,
// where parent node must come before the child node in
Expand All @@ -88,11 +95,11 @@ void AccessibilityBridge::CommitUpdates() {
}
}

tree_.Unserialize(update);
tree_->Unserialize(update);
pending_semantics_node_updates_.clear();
pending_semantics_custom_action_updates_.clear();

std::string error = tree_.error();
std::string error = tree_->error();
if (!error.empty()) {
FML_LOG(ERROR) << "Failed to update ui::AXTree, error: " << error;
return;
Expand Down Expand Up @@ -122,7 +129,7 @@ AccessibilityBridge::GetFlutterPlatformNodeDelegateFromID(
}

const ui::AXTreeData& AccessibilityBridge::GetAXTreeData() const {
return tree_.data();
return tree_->data();
}

const std::vector<ui::AXEventGenerator::TargetedEvent>
Expand Down Expand Up @@ -201,7 +208,7 @@ AccessibilityBridge::CreateRemoveReparentedNodesUpdate() {
for (auto node_update : pending_semantics_node_updates_) {
for (int32_t child_id : node_update.second.children_in_traversal_order) {
// Skip nodes that don't exist or have a parent in the current tree.
ui::AXNode* child = tree_.GetFromId(child_id);
ui::AXNode* child = tree_->GetFromId(child_id);
if (!child) {
continue;
}
Expand All @@ -222,7 +229,7 @@ AccessibilityBridge::CreateRemoveReparentedNodesUpdate() {
// Create an update to remove the child from its previous parent.
int32_t parent_id = child->parent()->id();
if (updates.find(parent_id) == updates.end()) {
updates[parent_id] = tree_.GetFromId(parent_id)->data();
updates[parent_id] = tree_->GetFromId(parent_id)->data();
}

ui::AXNodeData* parent = &updates[parent_id];
Expand All @@ -239,7 +246,7 @@ AccessibilityBridge::CreateRemoveReparentedNodesUpdate() {
}

ui::AXTreeUpdate update{
.tree_data = tree_.data(),
.tree_data = tree_->data(),
.nodes = std::vector<ui::AXNodeData>(),
};

Expand Down Expand Up @@ -649,8 +656,60 @@ gfx::NativeViewAccessible AccessibilityBridge::GetNativeAccessibleFromId(
gfx::RectF AccessibilityBridge::RelativeToGlobalBounds(const ui::AXNode* node,
bool& offscreen,
bool clip_bounds) {
return tree_.RelativeToTreeBounds(node, gfx::RectF(), &offscreen,
clip_bounds);
return tree_->RelativeToTreeBounds(node, gfx::RectF(), &offscreen,
clip_bounds);
}

ui::AXNode* AccessibilityBridge::GetNodeFromTree(
ui::AXTreeID tree_id,
ui::AXNode::AXID node_id) const {
return GetNodeFromTree(node_id);
}

ui::AXNode* AccessibilityBridge::GetNodeFromTree(
ui::AXNode::AXID node_id) const {
return tree_->GetFromId(node_id);
}

ui::AXTreeID AccessibilityBridge::GetTreeID() const {
return tree_->GetAXTreeID();
}

ui::AXTreeID AccessibilityBridge::GetParentTreeID() const {
return ui::AXTreeIDUnknown();
}

ui::AXNode* AccessibilityBridge::GetRootAsAXNode() const {
return tree_->root();
}

ui::AXNode* AccessibilityBridge::GetParentNodeFromParentTreeAsAXNode() const {
return nullptr;
}

ui::AXTree* AccessibilityBridge::GetTree() const {
return tree_.get();
}

ui::AXPlatformNode* AccessibilityBridge::GetPlatformNodeFromTree(
const ui::AXNode::AXID node_id) const {
auto platform_delegate_weak = GetFlutterPlatformNodeDelegateFromID(node_id);
auto platform_delegate = platform_delegate_weak.lock();
if (!platform_delegate) {
return nullptr;
}
return platform_delegate->GetPlatformNode();
}

ui::AXPlatformNode* AccessibilityBridge::GetPlatformNodeFromTree(
const ui::AXNode& node) const {
return GetPlatformNodeFromTree(node.id());
}

ui::AXPlatformNodeDelegate* AccessibilityBridge::RootDelegate() const {
return GetFlutterPlatformNodeDelegateFromID(GetRootAsAXNode()->id())
.lock()
.get();
}

} // namespace flutter
37 changes: 36 additions & 1 deletion shell/platform/common/accessibility_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "flutter/third_party/accessibility/ax/ax_tree.h"
#include "flutter/third_party/accessibility/ax/ax_tree_observer.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_tree_manager.h"

#include "flutter_platform_node_delegate.h"

Expand All @@ -39,6 +40,7 @@ namespace flutter {
class AccessibilityBridge
: public std::enable_shared_from_this<AccessibilityBridge>,
public FlutterPlatformNodeDelegate::OwnerBridge,
public ui::AXPlatformTreeManager,
private ui::AXTreeObserver {
public:
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -106,6 +108,39 @@ class AccessibilityBridge
const std::vector<ui::AXEventGenerator::TargetedEvent> GetPendingEvents()
const;

// |AXTreeManager|
ui::AXNode* GetNodeFromTree(const ui::AXTreeID tree_id,
const ui::AXNode::AXID node_id) const override;

// |AXTreeManager|
ui::AXNode* GetNodeFromTree(const ui::AXNode::AXID node_id) const override;

// |AXTreeManager|
ui::AXTreeID GetTreeID() const override;

// |AXTreeManager|
ui::AXTreeID GetParentTreeID() const override;

// |AXTreeManager|
ui::AXNode* GetRootAsAXNode() const override;

// |AXTreeManager|
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;

// |AXTreeManager|
ui::AXTree* GetTree() const override;

// |AXPlatformTreeManager|
ui::AXPlatformNode* GetPlatformNodeFromTree(
const ui::AXNode::AXID node_id) const override;

// |AXPlatformTreeManager|
ui::AXPlatformNode* GetPlatformNodeFromTree(
const ui::AXNode& node) const override;

// |AXPlatformTreeManager|
ui::AXPlatformNodeDelegate* RootDelegate() const override;

protected:
//---------------------------------------------------------------------------
/// @brief Handle accessibility events generated due to accessibility
Expand Down Expand Up @@ -176,7 +211,7 @@ class AccessibilityBridge
std::unordered_map<AccessibilityNodeId,
std::shared_ptr<FlutterPlatformNodeDelegate>>
id_wrapper_map_;
ui::AXTree tree_;
std::unique_ptr<ui::AXTree> tree_;
ui::AXEventGenerator event_generator_;
std::unordered_map<int32_t, SemanticsNode> pending_semantics_node_updates_;
std::unordered_map<int32_t, SemanticsCustomAction>
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/common/accessibility_bridge_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "flutter/third_party/accessibility/ax/ax_tree_manager_map.h"
#include "test_accessibility_bridge.h"

namespace flutter {
Expand Down Expand Up @@ -483,5 +484,15 @@ TEST(AccessibilityBridgeTest, CanReparentNodeWithChild) {
Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1));
}

TEST(AccessibilityBridgeTest, AXTreeManagerTest) {
std::shared_ptr<TestAccessibilityBridge> bridge =
std::make_shared<TestAccessibilityBridge>();

ui::AXTreeID tree_id = bridge->GetTreeID();
ui::AXTreeManager* manager =
ui::AXTreeManagerMap::GetInstance().GetManager(tree_id);
ASSERT_EQ(manager, static_cast<ui::AXTreeManager*>(bridge.get()));
}

} // namespace testing
} // namespace flutter
4 changes: 4 additions & 0 deletions shell/platform/common/flutter_platform_node_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,8 @@ FlutterPlatformNodeDelegate::GetOwnerBridge() const {
return bridge_;
}

ui::AXPlatformNode* FlutterPlatformNodeDelegate::GetPlatformNode() const {
return nullptr;
}

} // namespace flutter
3 changes: 3 additions & 0 deletions shell/platform/common/flutter_platform_node_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ class FlutterPlatformNodeDelegate : public ui::AXPlatformNodeDelegateBase {
/// platform thread.
std::weak_ptr<OwnerBridge> GetOwnerBridge() const;

// Get the platform node represented by this delegate.
virtual ui::AXPlatformNode* GetPlatformNode() const;

private:
ui::AXNode* ax_node_;
std::weak_ptr<OwnerBridge> bridge_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,9 @@ FlutterPlatformNodeDelegateWindows::GetTargetForNativeAccessibilityEvent() {
return view_->GetPlatformWindow();
}

ui::AXPlatformNode* FlutterPlatformNodeDelegateWindows::GetPlatformNode()
const {
return ax_platform_node_;
}

} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class FlutterPlatformNodeDelegateWindows : public FlutterPlatformNodeDelegate {
// | AXPlatformNodeDelegate |
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;

// | FlutterPlatformNodeDelegate |
ui::AXPlatformNode* GetPlatformNode() const override;

private:
ui::AXPlatformNode* ax_platform_node_;
std::weak_ptr<AccessibilityBridge> bridge_;
Expand Down

0 comments on commit c0b3f8f

Please sign in to comment.