// 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 UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_H_
#define UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_H_

#include <memory>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/ax_unique_id.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/accessibility/ax_virtual_view.h"
#include "ui/views/views_export.h"

namespace ui {

class AXPlatformNodeDelegate;

}  // namespace ui

namespace views {

class View;
class ViewsAXTreeManager;
class Widget;

// An object that manages the accessibility interface for a View.
//
// The default accessibility properties of a View is determined by calling
// |View::GetAccessibleNodeData()|, which is overridden by many |View|
// subclasses. |ViewAccessibility| lets you override these for a particular
// view.
//
// In most cases, subclasses of |ViewAccessibility| own the |AXPlatformNode|
// that implements the native accessibility APIs on a specific platform.
class VIEWS_EXPORT ViewAccessibility {
 public:
  using AccessibilityEventsCallback =
      base::RepeatingCallback<void(const ui::AXPlatformNodeDelegate*,
                                   const ax::mojom::Event)>;
  using AXVirtualViews = AXVirtualView::AXVirtualViews;

  static std::unique_ptr<ViewAccessibility> Create(View* view);

  ViewAccessibility(const ViewAccessibility&) = delete;
  ViewAccessibility& operator=(const ViewAccessibility&) = delete;
  virtual ~ViewAccessibility();

  // Modifies |node_data| to reflect the current accessible state of the
  // associated View, taking any custom overrides into account
  // (see OverrideFocus, OverrideRole, etc. below).
  virtual void GetAccessibleNodeData(ui::AXNodeData* node_data) const;

  //
  // The following methods get or set accessibility attributes (in the owning
  // View's AXNodeData), overrideing any identical attributes which might have
  // been set by the owning View in its View::GetAccessibleNodeData() method.
  //
  // Note that accessibility string attributes are only used if non-empty, so
  // you can't override a string with the empty string.
  //

  // Sets one of our virtual descendants as having the accessibility focus. This
  // means that if this view has the system focus, it will set the accessibility
  // focus to the provided descendant virtual view instead. Set this to nullptr
  // if none of our virtual descendants should have the accessibility focus. It
  // is illegal to set this to any virtual view that is currently not one of our
  // descendants and this is enforced by a DCHECK.
  void OverrideFocus(AXVirtualView* virtual_view);

  // Returns whether this view is focusable when the user uses an accessibility
  // aid or the keyboard, even though it may not be normally focusable. Note
  // that if using the keyboard, on macOS the preference "Full Keyboard Access"
  // needs to be turned on.
  virtual bool IsAccessibilityFocusable() const;

  // Used for testing. Returns true if this view is considered focused.
  virtual bool IsFocusedForTesting() const;

  // Call when this is the active descendant of a popup view that temporarily
  // takes over focus. It is only necessary to use this for menus like autofill,
  // where the actual focus is in content.
  // When the popup closes, call EndPopupFocusOverride().
  virtual void SetPopupFocusOverride();

  // Call when popup closes, if it used SetPopupFocusOverride().
  virtual void EndPopupFocusOverride();

  // Call when a menu closes, to restore focus to where it was previously.
  virtual void FireFocusAfterMenuClose();

  void OverrideRole(const ax::mojom::Role role);

  // Sets the accessible name to the specified string value.
  // By default the source type of the name is attribute. This source is
  // appropriate for most use cases where a View is providing a non-empty flat
  // string as the accessible name. If a View has a need to remove the
  // accessible name, the string should be empty and the source of the name
  // should instead be kAttributeExplicitlyEmpty. Note that the name source
  // types were created based on needs associated with web content
  // accessibility, and assistive technologies may make decisions based on that
  // supposition. For instance, kTitle implies that the source of the name will
  // be presented as a tooltip, such as would result from the HTML 'title'
  // attribute or the SVG <title> element.
  void OverrideName(
      const std::string& name,
      const ax::mojom::NameFrom name_from = ax::mojom::NameFrom::kAttribute);
  void OverrideName(
      const std::u16string& name,
      const ax::mojom::NameFrom name_from = ax::mojom::NameFrom::kAttribute);

  // Sets the accessible label source by establishing a relationship between
  // this View and another view, such as a Label. By default the source type of
  // the name is "related element." This default should cover most, if not all,
  // of the use cases for Views. Note that the name source types were created
  // based on needs associated with web content accessibility, and assistive
  // technologies may make decisions based on that supposition. For instance,
  // kTitle implies that the source of the name will be presented as a tooltip,
  // such as would result from the HTML 'title' attribute or the SVG <title>
  // element.
  void OverrideLabelledBy(const View* labelled_by_view,
                          const ax::mojom::NameFrom name_from =
                              ax::mojom::NameFrom::kRelatedElement);

  // Sets the accessible description to the specified string value.
  // By default the source type of the description is aria-description. While
  // Views technically don't support ARIA, aria-description is the closest
  // existing DescriptionFrom source for Views providing a flat string
  // description. And assistive technologies already know how to recognize this
  // source type. Therefore, Views are encouraged to go with this default unless
  // they have a specific reason not to. If a View has a need to remove the
  // accessible description, the string should be empty and the source of the
  // description should instead be kAttributeExplicitlyEmpty. If a View never
  // had an accessible description, there is no need to override it with an
  // empty string.
  void OverrideDescription(const std::string& description,
                           const ax::mojom::DescriptionFrom description_from =
                               ax::mojom::DescriptionFrom::kAriaDescription);
  void OverrideDescription(const std::u16string& description,
                           const ax::mojom::DescriptionFrom description_from =
                               ax::mojom::DescriptionFrom::kAriaDescription);

  // Sets the platform-specific accessible name/title property of the
  // NativeViewAccessible window. This is needed on platforms where the name
  // of the NativeViewAccessible window is automatically calculated by the
  // platform's accessibility API. For instance on the Mac, the label of the
  // NativeWidgetMacNSWindow of a JavaScript alert is taken from the name of
  // the child RootView. Note: the first function does the string conversion
  // and calls the second, thus only the latter needs to be implemented by
  // interested platforms.
  void OverrideNativeWindowTitle(const std::u16string& title);
  virtual void OverrideNativeWindowTitle(const std::string& title);

  // Sets whether this View hides all its descendants from the accessibility
  // tree that is exposed to platform APIs. This is similar, but not exactly
  // identical to aria-hidden="true".
  //
  // Note that this attribute does not cross widget boundaries, i.e. if a sub
  // widget is a descendant of this View, it will not be marked hidden. This
  // should not happen in practice as widgets are not children of Views.
  void OverrideIsLeaf(bool value);
  virtual bool IsLeaf() const;

  // Returns true if an ancestor of this node (not including itself) is a
  // leaf node, meaning that this node is not actually exposed to any
  // platform's accessibility layer.
  virtual bool IsChildOfLeaf() const;

  // Hides this View from the accessibility tree that is exposed to platform
  // APIs.
  void OverrideIsIgnored(bool value);
  virtual bool IsIgnored() const;

  // Marks this View either as enabled or disabled (grayed out) in the
  // accessibility tree and ignores the View's real enabled state. Does not
  // affect the View's focusable state (see "IsAccessibilityFocusable()").
  // Screen readers make a special announcement when an item is disabled.
  //
  // It might not be advisable to mark a View as enabled in the accessibility
  // tree, whilst the real View is actually disabled, because such a View will
  // not respond to user actions.
  void OverrideIsEnabled(bool enabled);
  virtual bool IsAccessibilityEnabled() const;

  void OverrideBounds(const gfx::RectF& bounds);
  void OverrideHasPopup(const ax::mojom::HasPopup has_popup);

  // Override information provided to users by screen readers when describing
  // elements in a menu, listbox, or another set-like item. For example, "New
  // tab, menu item 1 of 5". If not specified, a view's index in its parent and
  // its parent's number of children provide the values for |pos_in_set| and
  // |set_size| respectively.
  //
  // Note that |pos_in_set| is one-based, i.e. it starts from 1 not 0.
  void OverridePosInSet(int pos_in_set, int set_size);
  void ClearPosInSetOverride();

  // Override the next or previous focused widget. Some assistive technologies,
  // such as screen readers, may utilize this information to transition focus
  // from the beginning or end of one widget to another when navigating by its
  // default navigation method.
  void OverrideNextFocus(Widget* widget);
  void OverridePreviousFocus(Widget* widget);
  Widget* GetNextWindowFocus() const;
  Widget* GetPreviousWindowFocus() const;

  // Override the child tree id.
  void OverrideChildTreeID(ui::AXTreeID tree_id);
  ui::AXTreeID GetChildTreeID() const;

  // Returns the accessibility object that represents the View whose
  // accessibility is managed by this instance. This may be an AXPlatformNode or
  // it may be a native accessible object implemented by another class.
  virtual gfx::NativeViewAccessible GetNativeObject() const;

  // Causes the screen reader to announce |text|. If the current user is not
  // using a screen reader, has no effect.
  virtual void AnnounceText(const std::u16string& text);

  virtual const ui::AXUniqueId& GetUniqueId() const;

  View* view() const { return view_; }
  AXVirtualView* FocusedVirtualChild() const { return focused_virtual_child_; }
  ViewsAXTreeManager* AXTreeManager() const;

  //
  // Methods for managing virtual views.
  //

  // Adds |virtual_view| as a child of this View. We take ownership of our
  // virtual children.
  void AddVirtualChildView(std::unique_ptr<AXVirtualView> virtual_view);

  // Adds |virtual_view| as a child of this View at an index.
  // We take ownership of our virtual children.
  void AddVirtualChildViewAt(std::unique_ptr<AXVirtualView> virtual_view,
                             size_t index);

  // Removes |virtual_view| from this View. The virtual view's parent will
  // change to nullptr. Hands ownership back to the caller.
  std::unique_ptr<AXVirtualView> RemoveVirtualChildView(
      AXVirtualView* virtual_view);

  // Removes all the virtual children from this View.
  // The virtual views are deleted.
  void RemoveAllVirtualChildViews();

  const AXVirtualViews& virtual_children() const { return virtual_children_; }

  // Returns true if |virtual_view| is contained within the hierarchy of this
  // View, even as an indirect descendant.
  bool Contains(const AXVirtualView* virtual_view) const;

  // Returns the index of |virtual_view|, or nullopt if |virtual_view| is not a
  // child of this View.
  absl::optional<size_t> GetIndexOf(const AXVirtualView* virtual_view) const;

  // Returns the native accessibility object associated with the AXVirtualView
  // descendant that is currently focused. If no virtual descendants are
  // present, or no virtual descendant has been marked as focused, returns the
  // native accessibility object associated with this view.
  gfx::NativeViewAccessible GetFocusedDescendant();

  // If true, moves accessibility focus to an ancestor.
  void set_propagate_focus_to_ancestor(bool value) {
    propagate_focus_to_ancestor_ = value;
  }

  bool propagate_focus_to_ancestor() { return propagate_focus_to_ancestor_; }

  // Used for testing. Allows a test to watch accessibility events.
  const AccessibilityEventsCallback& accessibility_events_callback() const;
  void set_accessibility_events_callback(AccessibilityEventsCallback callback);

 protected:
  explicit ViewAccessibility(View* view);

  // Used internally and by View.
  virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type);

  // Used for testing. Called every time an accessibility event is fired.
  AccessibilityEventsCallback accessibility_events_callback_;

 private:
  friend class View;

  // Weak. Owns this.
  const raw_ptr<View> view_;

  // If there are any virtual children, they override any real children.
  // We own our virtual children.
  AXVirtualViews virtual_children_;

  // The virtual child that is currently focused.
  // This is nullptr if no virtual child is focused.
  // See also OverrideFocus() and GetFocusedDescendant().
  raw_ptr<AXVirtualView> focused_virtual_child_;

  const ui::AXUniqueId unique_id_;

  // Contains data set explicitly via OverrideRole, OverrideName, etc. that
  // overrides anything provided by GetAccessibleNodeData().
  ui::AXNodeData custom_data_;

  // If set to true, anything that is a descendant of this view will be hidden
  // from accessibility.
  bool is_leaf_ = false;

  // When true the view is ignored when generating the AX node hierarchy, but
  // its children are included. For example, if you created a custom table with
  // the digits 1 - 9 arranged in a 3 x 3 grid, marking the table and rows
  // "ignored" would mean that the digits 1 - 9 would appear as if they were
  // immediate children of the root. Likewise "internal" container views can be
  // ignored, like a Widget's RootView, ClientView, etc.
  // Similar to setting the role of an ARIA widget to "none" or
  // "presentational".
  bool is_ignored_ = false;

  // Used to override the View's enabled state in case we need to mark the View
  // as enabled or disabled only in the accessibility tree.
  absl::optional<bool> is_enabled_ = absl::nullopt;

  // Used by the Views system to help some assistive technologies, such as
  // screen readers, transition focus from one widget to another.
  base::WeakPtr<Widget> next_focus_ = nullptr;
  base::WeakPtr<Widget> previous_focus_ = nullptr;

  // This view's child tree id.
  absl::optional<ui::AXTreeID> child_tree_id_;

  // Whether to move accessibility focus to an ancestor.
  bool propagate_focus_to_ancestor_ = false;

#if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH)
  // Each instance of ViewAccessibility that's associated with a root View
  // owns an ViewsAXTreeManager. For other Views, this should be nullptr.
  std::unique_ptr<views::ViewsAXTreeManager> ax_tree_manager_;
#endif
};

}  // namespace views

#endif  // UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_H_