| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef FLUTTER_SHELL_PLATFORM_COMMON_ACCESSIBILITY_BRIDGE_H_ |
| #define FLUTTER_SHELL_PLATFORM_COMMON_ACCESSIBILITY_BRIDGE_H_ |
| |
| #include <unordered_map> |
| |
| #include "flutter/fml/mapping.h" |
| #include "flutter/shell/platform/embedder/embedder.h" |
| |
| #include "flutter/third_party/accessibility/ax/ax_event_generator.h" |
| #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" |
| |
| namespace flutter { |
| |
| //------------------------------------------------------------------------------ |
| /// Use this class to maintain an accessibility tree. This class consumes |
| /// semantics updates from the embedder API and produces an accessibility tree |
| /// in the native format. |
| /// |
| /// The bridge creates an AXTree to hold the semantics data that comes from |
| /// Flutter semantics updates. The tree holds AXNode[s] which contain the |
| /// semantics information for semantics node. The AXTree resemble the Flutter |
| /// semantics tree in the Flutter framework. The bridge also uses |
| /// FlutterPlatformNodeDelegate to wrap each AXNode in order to provide |
| /// an accessibility tree in the native format. |
| /// |
| /// To use this class, one must subclass this class and provide their own |
| /// implementation of FlutterPlatformNodeDelegate. |
| /// |
| /// AccessibilityBridge must be created as a shared_ptr, since some methods |
| /// acquires its weak_ptr. |
| class AccessibilityBridge |
| : public std::enable_shared_from_this<AccessibilityBridge>, |
| public FlutterPlatformNodeDelegate::OwnerBridge, |
| public ui::AXPlatformTreeManager, |
| private ui::AXTreeObserver { |
| public: |
| //----------------------------------------------------------------------------- |
| /// @brief Creates a new instance of a accessibility bridge. |
| AccessibilityBridge(); |
| virtual ~AccessibilityBridge(); |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Adds a semantics node update to the pending semantics update. |
| /// Calling this method alone will NOT update the semantics tree. |
| /// To flush the pending updates, call the CommitUpdates(). |
| /// |
| /// @param[in] node A reference to the semantics node update. |
| void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode2& node); |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Adds a custom semantics action update to the pending semantics |
| /// update. Calling this method alone will NOT update the |
| /// semantics tree. To flush the pending updates, call the |
| /// CommitUpdates(). |
| /// |
| /// @param[in] action A reference to the custom semantics action |
| /// update. |
| void AddFlutterSemanticsCustomActionUpdate( |
| const FlutterSemanticsCustomAction2& action); |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Flushes the pending updates and applies them to this |
| /// accessibility bridge. Calling this with no pending updates |
| /// does nothing, and callers should call this method at the end |
| /// of an atomic batch to avoid leaving the tree in a unstable |
| /// state. For example if a node reparents from A to B, callers |
| /// should only call this method when both removal from A and |
| /// addition to B are in the pending updates. |
| void CommitUpdates(); |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Get the flutter platform node delegate with the given id from |
| /// this accessibility bridge. Returns expired weak_ptr if the |
| /// delegate associated with the id does not exist or has been |
| /// removed from the accessibility tree. |
| /// |
| /// @param[in] id The id of the flutter accessibility node you want |
| /// to retrieve. |
| std::weak_ptr<FlutterPlatformNodeDelegate> |
| GetFlutterPlatformNodeDelegateFromID(AccessibilityNodeId id) const; |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Get the ax tree data from this accessibility bridge. The tree |
| /// data contains information such as the id of the node that |
| /// has the keyboard focus or the text selection range. |
| const ui::AXTreeData& GetAXTreeData() const; |
| |
| //------------------------------------------------------------------------------ |
| /// @brief Gets all pending accessibility events generated during |
| /// semantics updates. This is useful when deciding how to handle |
| /// events in AccessibilityBridgeDelegate::OnAccessibilityEvent in |
| /// case one may decide to handle an event differently based on |
| /// all pending events. |
| 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 |
| /// tree changes. These events are needed to be sent to native |
| /// accessibility system. See ui::AXEventGenerator::Event for |
| /// possible events. |
| /// |
| /// @param[in] targeted_event The object that contains both the |
| /// generated event and the event target. |
| virtual void OnAccessibilityEvent( |
| ui::AXEventGenerator::TargetedEvent targeted_event) = 0; |
| |
| //--------------------------------------------------------------------------- |
| /// @brief Creates a platform specific FlutterPlatformNodeDelegate. |
| /// Ownership passes to the caller. This method will be called |
| /// whenever a new AXNode is created in AXTree. Each platform |
| /// needs to implement this method in order to inject its |
| /// subclass into the accessibility bridge. |
| virtual std::shared_ptr<FlutterPlatformNodeDelegate> |
| CreateFlutterPlatformNodeDelegate() = 0; |
| |
| private: |
| // See FlutterSemanticsNode in embedder.h |
| typedef struct { |
| int32_t id; |
| FlutterSemanticsFlag flags; |
| FlutterSemanticsAction actions; |
| int32_t text_selection_base; |
| int32_t text_selection_extent; |
| int32_t scroll_child_count; |
| int32_t scroll_index; |
| double scroll_position; |
| double scroll_extent_max; |
| double scroll_extent_min; |
| double elevation; |
| double thickness; |
| std::string label; |
| std::string hint; |
| std::string value; |
| std::string increased_value; |
| std::string decreased_value; |
| std::string tooltip; |
| FlutterTextDirection text_direction; |
| FlutterRect rect; |
| FlutterTransformation transform; |
| std::vector<int32_t> children_in_traversal_order; |
| std::vector<int32_t> custom_accessibility_actions; |
| } SemanticsNode; |
| |
| // See FlutterSemanticsCustomAction in embedder.h |
| typedef struct { |
| int32_t id; |
| FlutterSemanticsAction override_action; |
| std::string label; |
| std::string hint; |
| } SemanticsCustomAction; |
| |
| std::unordered_map<AccessibilityNodeId, |
| std::shared_ptr<FlutterPlatformNodeDelegate>> |
| id_wrapper_map_; |
| 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> |
| pending_semantics_custom_action_updates_; |
| AccessibilityNodeId last_focused_id_ = ui::AXNode::kInvalidAXID; |
| |
| void InitAXTree(const ui::AXTreeUpdate& initial_state); |
| |
| // Create an update that removes any nodes that will be reparented by |
| // pending_semantics_updates_. Returns std::nullopt if none are reparented. |
| std::optional<ui::AXTreeUpdate> CreateRemoveReparentedNodesUpdate(); |
| |
| void GetSubTreeList(const SemanticsNode& target, |
| std::vector<SemanticsNode>& result); |
| void ConvertFlutterUpdate(const SemanticsNode& node, |
| ui::AXTreeUpdate& tree_update); |
| void SetRoleFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetStateFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetActionsFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetBooleanAttributesFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetIntAttributesFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetIntListAttributesFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetStringListAttributesFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetNameFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetValueFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetTooltipFromFlutterUpdate(ui::AXNodeData& node_data, |
| const SemanticsNode& node); |
| void SetTreeData(const SemanticsNode& node, ui::AXTreeUpdate& tree_update); |
| SemanticsNode FromFlutterSemanticsNode( |
| const FlutterSemanticsNode2& flutter_node); |
| SemanticsCustomAction FromFlutterSemanticsCustomAction( |
| const FlutterSemanticsCustomAction2& flutter_custom_action); |
| |
| // |AXTreeObserver| |
| void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; |
| |
| // |AXTreeObserver| |
| void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; |
| |
| // |AXTreeObserver| |
| void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override; |
| |
| // |AXTreeObserver| |
| void OnNodeDeleted(ui::AXTree* tree, AccessibilityNodeId node_id) override; |
| |
| // |AXTreeObserver| |
| void OnNodeReparented(ui::AXTree* tree, ui::AXNode* node) override; |
| |
| // |AXTreeObserver| |
| void OnRoleChanged(ui::AXTree* tree, |
| ui::AXNode* node, |
| ax::mojom::Role old_role, |
| ax::mojom::Role new_role) override; |
| |
| // |AXTreeObserver| |
| void OnAtomicUpdateFinished( |
| ui::AXTree* tree, |
| bool root_changed, |
| const std::vector<ui::AXTreeObserver::Change>& changes) override; |
| |
| // |FlutterPlatformNodeDelegate::OwnerBridge| |
| void SetLastFocusedId(AccessibilityNodeId node_id) override; |
| |
| // |FlutterPlatformNodeDelegate::OwnerBridge| |
| AccessibilityNodeId GetLastFocusedId() override; |
| |
| // |FlutterPlatformNodeDelegate::OwnerBridge| |
| gfx::NativeViewAccessible GetNativeAccessibleFromId( |
| AccessibilityNodeId id) override; |
| |
| // |FlutterPlatformNodeDelegate::OwnerBridge| |
| gfx::RectF RelativeToGlobalBounds(const ui::AXNode* node, |
| bool& offscreen, |
| bool clip_bounds) override; |
| |
| BASE_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge); |
| }; |
| |
| } // namespace flutter |
| |
| #endif // FLUTTER_SHELL_PLATFORM_COMMON_ACCESSIBILITY_BRIDGE_H_ |