// 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 #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_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, public FlutterPlatformNodeDelegate::OwnerBridge, private ui::AXTreeObserver { public: //----------------------------------------------------------------------------- /// @brief Creates a new instance of a accessibility bridge. AccessibilityBridge(); virtual ~AccessibilityBridge(); //----------------------------------------------------------------------------- /// @brief The ID of the root node in the accessibility tree. In Flutter, // this is always 0. static constexpr int32_t kRootNodeId = 0; //------------------------------------------------------------------------------ /// @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 pointer to the semantics node update. void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode* 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 pointer to the custom semantics action /// update. void AddFlutterSemanticsCustomActionUpdate( const FlutterSemanticsCustomAction* 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 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 GetPendingEvents() const; 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 CreateFlutterPlatformNodeDelegate() = 0; //------------------------------------------------------------------------------ /// @brief Recreate all FlutterPlatformNodeDelegates. /// /// This can be useful for subclasses when updating some /// properties that are used by node delegates, such as views. /// Each node is recreated using /// CreateFlutterPlatformNodeDelegate, then initialized using /// AXNodes from their corresponding old one. void RecreateNodeDelegates(); 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 children_in_traversal_order; std::vector 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> id_wrapper_map_; ui::AXTree tree_; ui::AXEventGenerator event_generator_; std::unordered_map pending_semantics_node_updates_; std::unordered_map 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 CreateRemoveReparentedNodesUpdate(); void GetSubTreeList(const SemanticsNode& target, std::vector& 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 FlutterSemanticsNode* flutter_node); SemanticsCustomAction FromFlutterSemanticsCustomAction( const FlutterSemanticsCustomAction* 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& 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_