blob: 91710e486f3169a7454956d16930d0ca90eed833 [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 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_