blob: 2290cd07cc0e935678605ccfad33f4ab8af89ad8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_ARC_ACCESSIBILITY_AX_TREE_SOURCE_ARC_H_
#define CHROME_BROWSER_ASH_ARC_ACCESSIBILITY_AX_TREE_SOURCE_ARC_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "ash/components/arc/mojom/accessibility_helper.mojom-forward.h"
#include "base/containers/flat_map.h"
#include "chrome/browser/ash/arc/accessibility/accessibility_info_data_wrapper.h"
#include "extensions/browser/api/automation_internal/automation_event_router.h"
#include "ui/accessibility/ax_action_handler.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/ax_tree_serializer.h"
#include "ui/accessibility/ax_tree_source.h"
#include "ui/views/view.h"
namespace aura {
class Window;
}
namespace arc {
class AXTreeSourceArcTest;
using AXTreeArcSerializer = ui::AXTreeSerializer<AccessibilityInfoDataWrapper*>;
// This class represents the accessibility tree from the focused ARC window.
class AXTreeSourceArc : public ui::AXTreeSource<AccessibilityInfoDataWrapper*>,
public ui::AXActionHandler {
public:
class Delegate {
public:
virtual void OnAction(const ui::AXActionData& data) const = 0;
virtual bool UseFullFocusMode() const = 0;
};
// The interface to hook the event handling and the node serialization.
class Hook {
public:
Hook() = default;
virtual ~Hook() = default;
// Called prior to accessibility event dispatch.
// Hook implementations can update the internal state if necessary so that
// hooks can update the serialization state in PostSerializeNode().
// Return true if re-serialization of attaching node is needed.
virtual bool PreDispatchEvent(
AXTreeSourceArc* tree_source,
const mojom::AccessibilityEventData& event_data) = 0;
// Called after the default serialization of the attaching node.
// Hook implementations can modify the serialization of given |out_data|.
// Note that serialization is executed only when ui::AXTreeSerializer calls
// SerializeNode() from AXTreeSerializer.SerializeChanges().
// To ensure the node re-serialized, the class must return |true| on
// PreDispatchEvent() if the event is NOT coming from its ancestry.
virtual void PostSerializeNode(ui::AXNodeData* out_data) const = 0;
};
AXTreeSourceArc(Delegate* delegate, aura::Window* window);
AXTreeSourceArc(const AXTreeSourceArc&) = delete;
AXTreeSourceArc& operator=(const AXTreeSourceArc&) = delete;
~AXTreeSourceArc() override;
// Notify automation of an accessibility event.
void NotifyAccessibilityEvent(mojom::AccessibilityEventData* event_data);
// Notify automation of a result to an action.
void NotifyActionResult(const ui::AXActionData& data, bool result);
// Notify automation of result to getTextLocation.
void NotifyGetTextLocationDataResult(const ui::AXActionData& data,
const absl::optional<gfx::Rect>& rect);
// Invalidates the tree serializer.
void InvalidateTree();
// When it is enabled, this class exposes an accessibility tree optimized for
// screen readers such as ChromeVox and SwitchAccess. This intends to have the
// navigation order and focusabilities similar to TalkBack.
// Also, when it is enabled, the accessibility focus in Android is exposed as
// the focus of this tree.
bool UseFullFocusMode() const;
// Returns true if the node id is the root of the node tree (which can have a
// parent window).
// virtual for testing.
virtual bool IsRootOfNodeTree(int32_t id) const;
AccessibilityInfoDataWrapper* GetFirstImportantAncestor(
AccessibilityInfoDataWrapper* info_data) const;
// AXTreeSource:
bool GetTreeData(ui::AXTreeData* data) const override;
AccessibilityInfoDataWrapper* GetRoot() const override;
AccessibilityInfoDataWrapper* GetFromId(int32_t id) const override;
AccessibilityInfoDataWrapper* GetParent(
AccessibilityInfoDataWrapper* info_data) const override;
void SerializeNode(AccessibilityInfoDataWrapper* info_data,
ui::AXNodeData* out_data) const override;
aura::Window* window() { return window_; }
void set_window(aura::Window* window) { window_ = window; }
bool is_notification() { return is_notification_; }
bool is_input_method_window() { return is_input_method_window_; }
// The window id of this tree.
absl::optional<int32_t> window_id() const { return window_id_; }
void set_automation_event_router_for_test(
extensions::AutomationEventRouterInterface* router) {
automation_event_router_for_test_ = router;
}
void set_window_id_for_test(int32_t window_id) { window_id_ = window_id; }
private:
friend class arc::AXTreeSourceArcTest;
// Actual implementation of NotifyAccessibilityEvent.
void NotifyAccessibilityEventInternal(
const mojom::AccessibilityEventData& event_data);
// Returns AutomationEventRouter.
extensions::AutomationEventRouterInterface* GetAutomationEventRouter() const;
// Computes the smallest rect that encloses all of the descendants of
// |info_data|.
gfx::Rect ComputeEnclosingBounds(
AccessibilityInfoDataWrapper* info_data) const;
// Helper to recursively compute bounds for |info_data|. Returns true if
// non-empty bounds were encountered.
void ComputeEnclosingBoundsInternal(AccessibilityInfoDataWrapper* info_data,
gfx::Rect* computed_bounds) const;
// Find the most top-left focusable node under the given node in full focus
// mode.
AccessibilityInfoDataWrapper* FindFirstFocusableNodeInFullFocusMode(
AccessibilityInfoDataWrapper* info_data) const;
// Updates android_focused_id_ from given AccessibilityEventData.
// Having this method, |android_focused_id_| is one of these:
// - input focus in Android
// - accessibility focus in Android
// - the chrome automation client's internal focus (via set sequential focus
// action and replying accessibility focus event from Android).
// This returns false if we don't want to dispatch the processing
// event to chrome automation. Otherwise, this returns true.
bool UpdateAndroidFocusedId(const mojom::AccessibilityEventData& event_data);
// Processes implementations of Hooks and returns a list node id that needs
// re-serialization.
std::vector<int32_t> ProcessHooksOnEvent(
const mojom::AccessibilityEventData& event_data);
// Resets tree state.
void Reset();
// Returns true if we want to traversal |left| after |right|.
// Note that this comparison is NOT transitive.
bool NeedReorder(AccessibilityInfoDataWrapper* left,
AccessibilityInfoDataWrapper* right) const;
// Returns true if we can traversal |left| before |right|.
bool CompareBounds(const gfx::Rect& left, const gfx::Rect& right) const;
// AXTreeSource:
int32_t GetId(AccessibilityInfoDataWrapper* info_data) const override;
void GetChildren(
AccessibilityInfoDataWrapper* info_data,
std::vector<AccessibilityInfoDataWrapper*>* out_children) const override;
bool IsIgnored(AccessibilityInfoDataWrapper* info_data) const override;
bool IsValid(AccessibilityInfoDataWrapper* info_data) const override;
bool IsEqual(AccessibilityInfoDataWrapper* info_data1,
AccessibilityInfoDataWrapper* info_data2) const override;
AccessibilityInfoDataWrapper* GetNull() const override;
// AXActionHandlerBase:
void PerformAction(const ui::AXActionData& data) override;
// Maps an AccessibilityInfoDataWrapper ID to its tree data.
std::map<int32_t, std::unique_ptr<AccessibilityInfoDataWrapper>> tree_map_;
// Maps an AccessibilityInfoDataWrapper ID to its parent.
std::map<int32_t, int32_t> parent_map_;
std::unique_ptr<AXTreeArcSerializer> current_tree_serializer_;
absl::optional<int32_t> root_id_;
absl::optional<int32_t> window_id_;
absl::optional<int32_t> android_focused_id_;
bool is_notification_;
bool is_input_method_window_;
absl::optional<std::string> notification_key_;
// Window corresponding this tree.
aura::Window* window_;
// Cache of mapping from the *Android* window id to the last focused node id.
std::map<int32_t, int32_t> window_id_to_last_focus_node_id_;
// Mapping from Chrome node ID to its cached computed bounds.
// This simplifies bounds calculations.
std::map<int32_t, gfx::Rect> computed_bounds_;
// Mapping from Chrome node ID to the attached hook implementations.
base::flat_map<int32_t, std::unique_ptr<Hook>> hooks_;
// A delegate that handles accessibility actions on behalf of this tree. The
// delegate is valid during the lifetime of this tree.
const Delegate* const delegate_;
extensions::AutomationEventRouterInterface*
automation_event_router_for_test_ = nullptr;
};
} // namespace arc
#endif // CHROME_BROWSER_ASH_ARC_ACCESSIBILITY_AX_TREE_SOURCE_ARC_H_