blob: 7e6698021b726befe1c100d0e0d910fd740abccd [file] [log] [blame]
// 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_