blob: 7a2b0a6c95d6940eb6066d8efe5cabdb9d3c938e [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_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.
///
/// This class takes in a AccessibilityBridgeDelegate instance and is in charge
/// of its lifecycle. The delegate are used to handle the accessibility events
/// and actions.
///
/// To use this class, you must provide your own implementation of
/// FlutterPlatformNodeDelegate and AccessibilityBridgeDelegate.
class AccessibilityBridge
: public std::enable_shared_from_this<AccessibilityBridge>,
public FlutterPlatformNodeDelegate::OwnerBridge,
private ui::AXTreeObserver {
public:
//-----------------------------------------------------------------------------
/// Delegate to handle requests from the accessibility bridge. The requests
/// include sending accessibility event to native accessibility system,
/// routing accessibility action to the Flutter framework, and creating
/// platform specific FlutterPlatformNodeDelegate.
///
/// The accessibility events are generated when accessibility tree changes.
/// These events must be sent to the native accessibility system through
/// the native API for the system to pick up the changes
/// (e.g. NSAccessibilityPostNotification in MacOS).
///
/// The accessibility actions are generated by the native accessibility system
/// when users interacted with the assistive technologies. Those actions
/// needed to be sent to the Flutter framework.
///
/// Each platform needs to implement the FlutterPlatformNodeDelegate and
/// returns its platform specific instance of FlutterPlatformNodeDelegate
/// in this delegate.
class AccessibilityBridgeDelegate {
public:
virtual ~AccessibilityBridgeDelegate() = default;
//---------------------------------------------------------------------------
/// @brief Handle accessibility events generated due to accessibility
/// tree changes. These events are generated in accessibility
/// bridge and 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 Dispatch accessibility action back to the Flutter framework.
/// These actions are generated in the native accessibility
/// system when users interact with the assistive technologies.
/// For example, a
/// FlutterSemanticsAction::kFlutterSemanticsActionTap is
/// fired when user click or touch the screen.
///
/// @param[in] target The semantics node id of the action
/// target.
/// @param[in] action The generated flutter semantics action.
/// @param[in] data Additional data associated with the
/// action.
virtual void DispatchAccessibilityAction(AccessibilityNodeId target,
FlutterSemanticsAction action,
fml::MallocMapping data) = 0;
//---------------------------------------------------------------------------
/// @brief Creates a platform specific FlutterPlatformNodeDelegate.
/// Ownership passes to the caller. This method will be called
/// by accessibility bridge 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;
};
//-----------------------------------------------------------------------------
/// @brief Creates a new instance of a accessibility bridge.
///
/// @param[in] user_data A custom pointer to the data of your
/// choice. This pointer can be retrieve later
/// through GetUserData().
AccessibilityBridge(std::unique_ptr<AccessibilityBridgeDelegate> delegate);
~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 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<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();
//------------------------------------------------------------------------------
/// @brief Update the AccessibilityBridgeDelegate stored in the
/// accessibility bridge to a new one.
void UpdateDelegate(std::unique_ptr<AccessibilityBridgeDelegate> delegate);
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;
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_;
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;
std::unique_ptr<AccessibilityBridgeDelegate> delegate_;
void InitAXTree(const ui::AXTreeUpdate& initial_state);
void GetSubTreeList(SemanticsNode target, std::vector<SemanticsNode>& result);
void ConvertFluterUpdate(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 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<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|
void DispatchAccessibilityAction(AccessibilityNodeId target,
FlutterSemanticsAction action,
fml::MallocMapping data) 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_