blob: 6888f133275270682eb87848eb7632964dc46650 [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_FUCHSIA_FLUTTER_ACCESSIBILITY_BRIDGE_H_
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_ACCESSIBILITY_BRIDGE_H_
// Work around symbol conflicts with ICU.
#undef TRUE
#undef FALSE
#include <fuchsia/accessibility/semantics/cpp/fidl.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/ui/gfx/cpp/fidl.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/sys/cpp/service_directory.h>
#include <zircon/types.h>
#include <memory>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "flutter/fml/macros.h"
#include "flutter/lib/ui/semantics/semantics_node.h"
namespace flutter_runner {
// Accessibility bridge.
//
// This class intermediates accessibility-related calls between Fuchsia and
// Flutter. It serves to resolve the impedance mismatch between Flutter's
// platform-agnostic accessibility APIs and Fuchsia's APIs and behaviour.
//
// This bridge performs the following functions, among others:
//
// * Translates Flutter's semantics node updates to events Fuchsia requires
// (e.g. Flutter only sends updates for changed nodes, but Fuchsia requires
// the entire flattened subtree to be sent when a node changes.
class AccessibilityBridge
: public fuchsia::accessibility::semantics::SemanticListener {
public:
// A delegate to call when semantics are enabled or disabled.
class Delegate {
public:
virtual void SetSemanticsEnabled(bool enabled) = 0;
virtual void DispatchSemanticsAction(int32_t node_id,
flutter::SemanticsAction action) = 0;
};
// TODO(MI4-2531, FIDL-718): Remove this. We shouldn't be worried about
// batching messages at this level.
// FIDL may encode a C++ struct as larger than the sizeof the C++ struct.
// This is to make sure we don't send updates that are too large.
static constexpr uint32_t kMaxMessageSize = ZX_CHANNEL_MAX_MSG_BYTES / 2;
static_assert(fuchsia::accessibility::semantics::MAX_LABEL_SIZE <
kMaxMessageSize - 1);
// Flutter uses signed 32 bit integers for node IDs, while Fuchsia uses
// unsigned 32 bit integers. A change in the size on either one would break
// casts and size tracking logic in the implementation.
static constexpr size_t kNodeIdSize = sizeof(flutter::SemanticsNode::id);
static_assert(
kNodeIdSize ==
sizeof(fuchsia::accessibility::semantics::Node().node_id()),
"flutter::SemanticsNode::id and "
"fuchsia::accessibility::semantics::Node::node_id differ in size.");
AccessibilityBridge(Delegate& delegate,
const std::shared_ptr<sys::ServiceDirectory> services,
fuchsia::ui::views::ViewRef view_ref);
// Returns true if accessible navigation is enabled.
bool GetSemanticsEnabled() const;
// Enables Flutter accessibility navigation features.
//
// Once enabled, any semantics updates in the Flutter application will
// trigger |FuchsiaAccessibility::DispatchAccessibilityEvent| callbacks
// to send events back to the Fuchsia SemanticsManager.
void SetSemanticsEnabled(bool enabled);
// Adds a semantics node update to the buffer of node updates to apply.
void AddSemanticsNodeUpdate(const flutter::SemanticsNodeUpdates update);
// Notifies the bridge of a 'hover move' touch exploration event.
zx_status_t OnHoverMove(double x, double y);
// |fuchsia::accessibility::semantics::SemanticListener|
void HitTest(
fuchsia::math::PointF local_point,
fuchsia::accessibility::semantics::SemanticListener::HitTestCallback
callback) override;
// |fuchsia::accessibility::semantics::SemanticListener|
void OnAccessibilityActionRequested(
uint32_t node_id,
fuchsia::accessibility::semantics::Action action,
fuchsia::accessibility::semantics::SemanticListener::
OnAccessibilityActionRequestedCallback callback) override;
private:
// Holds only the fields we need for hit testing.
// In particular, it adds a screen_rect field to flutter::SemanticsNode.
struct SemanticsNode {
int32_t id;
int32_t flags;
SkRect rect;
SkRect screen_rect;
SkM44 transform;
std::vector<int32_t> children_in_hit_test_order;
};
AccessibilityBridge::Delegate& delegate_;
static constexpr int32_t kRootNodeId = 0;
fidl::Binding<fuchsia::accessibility::semantics::SemanticListener> binding_;
fuchsia::accessibility::semantics::SemanticsManagerPtr
fuchsia_semantics_manager_;
fuchsia::accessibility::semantics::SemanticTreePtr tree_ptr_;
bool semantics_enabled_;
// This is the cache of all nodes we've sent to Fuchsia's SemanticsManager.
// Assists with pruning unreachable nodes and hit testing.
std::unordered_map<int32_t, SemanticsNode> nodes_;
// Derives the BoundingBox of a Flutter semantics node from its
// rect and elevation.
fuchsia::ui::gfx::BoundingBox GetNodeLocation(
const flutter::SemanticsNode& node) const;
// Converts a Flutter semantics node's transformation to a mat4.
fuchsia::ui::gfx::mat4 GetNodeTransform(
const flutter::SemanticsNode& node) const;
// Derives the attributes for a Fuchsia semantics node from a Flutter
// semantics node.
fuchsia::accessibility::semantics::Attributes GetNodeAttributes(
const flutter::SemanticsNode& node,
size_t* added_size) const;
// Derives the states for a Fuchsia semantics node from a Flutter semantics
// node.
fuchsia::accessibility::semantics::States GetNodeStates(
const flutter::SemanticsNode& node,
size_t* additional_size) const;
// Gets the set of reachable descendants from the given node id.
std::unordered_set<int32_t> GetDescendants(int32_t node_id) const;
// Removes internal references to any dangling nodes from previous
// updates, and updates the Accessibility service.
//
// May result in a call to FuchsiaAccessibility::Commit().
void PruneUnreachableNodes();
// Updates the on-screen positions of accessibility elements,
// starting from the root element with an identity matrix.
//
// This should be called from Update.
void UpdateScreenRects();
// Updates the on-screen positions of accessibility elements, starting
// from node_id and using the specified transform.
//
// Update calls this via UpdateScreenRects().
void UpdateScreenRects(int32_t node_id,
SkM44 parent_transform,
std::unordered_set<int32_t>* visited_nodes);
// Traverses the semantics tree to find the node_id hit by the given x,y
// point.
//
// Assumes that SemanticsNode::screen_rect is up to date.
std::optional<int32_t> GetHitNode(int32_t node_id, float x, float y);
// Converts a fuchsia::accessibility::semantics::Action to a
// flutter::SemanticsAction.
//
// The node_id parameter is used for printing warnings about unsupported
// action types.
std::optional<flutter::SemanticsAction> GetFlutterSemanticsAction(
fuchsia::accessibility::semantics::Action fuchsia_action,
uint32_t node_id);
// |fuchsia::accessibility::semantics::SemanticListener|
void OnSemanticsModeChanged(bool enabled,
OnSemanticsModeChangedCallback callback) override;
FML_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge);
};
} // namespace flutter_runner
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_ACCESSIBILITY_BRIDGE_H_