| // 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 <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "build/build_config.h" |
| #include "ui/accessibility/ax_enums.mojom-forward.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/accessibility/platform/ax_platform_node_id.h" |
| #include "ui/accessibility/platform/ax_unique_id.h" |
| #include "ui/gfx/native_widget_types.h" |
| #include "ui/views/accessibility/ax_attribute_changed_callbacks.h" |
| #include "ui/views/accessibility/view_accessibility_utils.h" |
| #include "ui/views/views_export.h" |
| #include "ui/views/widget/widget_observer.h" |
| |
| namespace ui { |
| |
| class AXPlatformNodeDelegate; |
| |
| } // namespace ui |
| |
| namespace views { |
| |
| class AtomicViewAXTreeManager; |
| class AXVirtualView; |
| class ScopedAccessibilityEventBlocker; |
| class View; |
| class Widget; |
| |
| using RoleCallbackList = base::RepeatingCallbackList<void(ax::mojom::Role)>; |
| using IntAttributeCallbackList = |
| base::RepeatingCallbackList<void(ax::mojom::IntAttribute, |
| std::optional<int>)>; |
| using StringAttributeCallbackList = |
| base::RepeatingCallbackList<void(ax::mojom::StringAttribute, |
| const std::optional<std::string>&)>; |
| using BoolAttributeCallbackList = |
| base::RepeatingCallbackList<void(ax::mojom::BoolAttribute, |
| std::optional<bool>)>; |
| using StateCallbackList = |
| base::RepeatingCallbackList<void(ax::mojom::State, bool)>; |
| using IntListAttributeCallbackList = base::RepeatingCallbackList<void( |
| ax::mojom::IntListAttribute, |
| const std::optional<std::vector<int32_t>>&)>; |
| |
| // 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 WidgetObserver { |
| public: |
| using AccessibilityEventsCallback = |
| base::RepeatingCallback<void(const ui::AXPlatformNodeDelegate*, |
| const ax::mojom::Event)>; |
| using AXVirtualViews = std::vector<std::unique_ptr<AXVirtualView>>; |
| |
| enum class State { kUninitialized, kInitializing, kInitialized }; |
| |
| static std::unique_ptr<ViewAccessibility> Create(View* view); |
| |
| ViewAccessibility(const ViewAccessibility&) = delete; |
| ViewAccessibility& operator=(const ViewAccessibility&) = delete; |
| ~ViewAccessibility() override; |
| |
| // Modifies |node_data| to reflect the current accessible state of the |
| // associated View, taking any custom overrides into account |
| // (see OverrideFocus, etc. below). |
| virtual void GetAccessibleNodeData(ui::AXNodeData* node_data) const; |
| |
| virtual void NotifyEvent(ax::mojom::Event event_type, bool send_native_event); |
| |
| // Made to be overridden on platforms that need the temporary |
| // `AtomicViewAXTreeManager` to enable more accessibility functionalities for |
| // Views. See crbug.com/1468416 for more info. |
| virtual void EnsureAtomicViewAXTreeManager() {} |
| |
| void SetIgnoreMissingWidgetForTesting(bool value) { |
| ignore_missing_widget_for_testing_ = value; |
| } |
| |
| // |
| // 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(); |
| |
| // Sets/gets whether or not this view's descendants should be included in |
| // the accessibility tree. It is the functional equivalent of calling |
| // `SetAccessibleIsIgnored` on each and every view descendant of this |
| // view. The default value is false, which is appropriate for most views. |
| // Note that you should not set this property if a view has no descendants. |
| // It is essential that you do not set this property to true on containers |
| // which have one or more descendants which are focusable or otherwise |
| // interactive as this would make those descendants completely inaccessible. |
| // If no value has been set, the "leafiness" of this view will be based on |
| // the type of children (if any). |
| void SetIsLeaf(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; |
| |
| void SetReadOnly(bool read_only); |
| |
| // Returns true if we heuristically pruned (ignored) this view from the |
| // accessibility tree. |
| bool GetIsPruned() const; |
| |
| void SetCharacterOffsets(const std::vector<int32_t>& offsets); |
| |
| const std::vector<int32_t>& GetCharacterOffsets() const; |
| |
| void SetWordStarts(const std::vector<int32_t>& offsets); |
| |
| const std::vector<int32_t>& GetWordStarts() const; |
| |
| void SetWordEnds(const std::vector<int32_t>& offsets); |
| |
| const std::vector<int32_t>& GetWordEnds() const; |
| |
| void ClearTextOffsets(); |
| |
| void SetControlIds(const std::vector<int32_t>& ids); |
| |
| void RemoveControlIds(); |
| |
| void SetClipsChildren(bool clips_children); |
| |
| void SetClassName(const std::string& class_name); |
| |
| void SetHasPopup(const ax::mojom::HasPopup has_popup); |
| |
| void SetRole(const ax::mojom::Role role); |
| |
| // Sets the accessible role along with a customized string to be used by |
| // assistive technologies to present the role. When there is no role |
| // description provided, assistive technologies will use either the default |
| // role descriptions we provide (which are currently located in a number of |
| // places. See crbug.com/1290866) or the value provided by their platform. As |
| // a general rule, it is preferable to not override the role string. Please |
| // seek review from accessibility OWNERs when using this function. |
| void SetRole(ax::mojom::Role role, const std::u16string& role_description); |
| |
| // This function cannot follow the established pattern and be named GetRole() |
| // because of a function of the same name in AXPlatformNodeDelegate. |
| // ViewAXPlatformNodeDelegate extends both ViewAccessibility and |
| // AXPlatformNodeDelegate, which would lead to conflicts and confusion. |
| // TODO(crbug.com/325137417): Rename to GetRole once the ViewsAX project is |
| // completed and we don't have ViewAXPlatformNodeDelegate anymore. |
| ax::mojom::Role GetCachedRole() const; |
| |
| void SetRoleDescription(const std::u16string& role_description); |
| void SetRoleDescription(const std::string& role_description); |
| |
| std::u16string GetRoleDescription() const; |
| |
| void RemoveRoleDescription(); |
| |
| // For the same reasons as GetCachedRole, this function cannot |
| // follow the established pattern and be named GetName() |
| // TODO(crbug.com/325137417): Rename to GetName once the ViewsAX project is |
| // completed and we don't have ViewAXPlatformNodeDelegate anymore. |
| std::u16string GetCachedName() const; |
| |
| // Returns the source type of the accessible name. |
| // |
| // This function cannot currently be named GetNameFrom() because of a function |
| // of the same name in AXPlatformNodeDelegate. ViewAXPlatformNodeDelegate |
| // extends both ViewAccessibility and AXPlatformNodeDelegate. |
| // TODO(crbug.com/325137417): Rename to GetNameFrom once the ViewsAX project |
| // is completed and we don't have ViewAXPlatformNodeDelegate anymore. |
| ax::mojom::NameFrom GetCachedNameFrom() const; |
| |
| // Sets the accessible name to the specified string and source type. |
| // To indicate that this view should never have an accessible name, e.g. to |
| // prevent screen readers from speaking redundant information, set the type to |
| // `kAttributeExplicitlyEmpty`. NOTE: Do not use `kAttributeExplicitlyEmpty` |
| // on a view which may or may not have a name depending on circumstances. Also |
| // please seek review from accessibility OWNERs when removing the name, |
| // especially for views which are focusable or otherwise interactive. |
| // The source type options are: |
| // |
| // * kNone: No name provided. |
| // * kAttribute: Name from a flat string (e.g. aria-label or View). This is |
| // the default value. |
| // * kAttributeExplicitlyEmpty: Name removed for accessibility reasons. |
| // * kCaption: Name from a table caption. |
| // * kContents: Name from the displayed text (e.g. label or link). |
| // * kPlaceholder: Name from a textfield placeholder. |
| // * kRelatedElement: Name from another object in the UI(e.g. figcaption or |
| // View). |
| // * kTitle: Name from a title attribute or element (HTML or SVG). |
| // * kValue: Name from a value attribute (e.g. button). |
| // * kPopoverAttribute: Name from a tooltip-style popover. |
| void SetName(std::u16string name, ax::mojom::NameFrom name_from); |
| void SetName(std::string_view name, ax::mojom::NameFrom name_from); |
| void SetName(std::u16string name); |
| void SetName(std::string_view name); |
| |
| // Sets the accessible name of this view to that of `naming_view`. Often |
| // `naming_view` is a `views::Label`, but any view with an accessible name |
| // will work. |
| void SetName(View& naming_view); |
| |
| // Removes kName and KNameFrom attributes from accessibility cache. |
| void RemoveName(); |
| |
| void SetIsEditable(bool editable); |
| |
| void SetBounds(const gfx::RectF& bounds); |
| |
| void SetIsSelected(bool selected); |
| |
| void SetIsMultiselectable(bool multiselectable); |
| |
| void SetIsModal(bool modal); |
| |
| void AddHTMLAttributes(std::pair<std::string, std::string> attribute); |
| |
| void SetIsHovered(bool is_hovered); |
| bool GetIsHovered() const; |
| |
| void SetPopupForId(ui::AXPlatformNodeId popup_for_id); |
| |
| void SetTextDirection(int text_direction); |
| |
| void SetIsProtected(bool is_protected); |
| |
| void SetTextSelStart(int32_t text_sel_start); |
| void SetTextSelEnd(int32_t text_sel_end); |
| |
| void SetLiveAtomic(bool live_atomic); |
| |
| void SetLiveStatus(const std::string& status); |
| |
| void SetLiveRelevant(const std::string& live_relevant); |
| void RemoveLiveRelevant(); |
| |
| void SetContainerLiveRelevant(const std::string& live_relevant); |
| void RemoveContainerLiveRelevant(); |
| |
| // Hides this view from the accessibility APIs. Keep in mind that this is not |
| // the sole determinant of whether the ignored state is set. See |
| // `UpdateIgnoredState`. |
| void SetIsIgnored(bool is_ignored); |
| bool GetIsIgnored() const; |
| |
| // Note that `pos_in_set` starts from 1 not 0. |
| void SetPosInSet(int pos_in_set); |
| void SetSetSize(int set_size); |
| void ClearPosInSet(); |
| void ClearSetSize(); |
| |
| void SetScrollX(int scroll_x); |
| void SetScrollXMin(int scroll_x_min); |
| void SetScrollXMax(int scroll_x_max); |
| void SetScrollY(int scroll_y); |
| void SetScrollYMin(int scroll_y_min); |
| void SetScrollYMax(int scroll_y_max); |
| void SetIsScrollable(bool scrollable); |
| |
| void SetActiveDescendant(views::View& view); |
| void SetActiveDescendant(ui::AXPlatformNodeId id); |
| void ClearActiveDescendant(); |
| |
| void SetIsInvisible(bool is_invisible); |
| void SetIsExpanded(); |
| void SetIsCollapsed(); |
| |
| // Sets the view's expanded and collapsed states back to false. Expanded and |
| // collapsed states are typically mutually exclusive; however, certain views, |
| // such as the notification header view, offer an extra feature wherein the |
| // view itself cannot be either expanded or collapsed. This occurs in |
| // situations where the view is considered invisible, and therefore not |
| // interactable. Therefore, in such situations, it was necessary to explicitly |
| // remove both expanded and collapsed states from the view accessibility |
| // cache. |
| void RemoveExpandCollapseState(); |
| |
| void SetIsVertical(bool vertical); |
| |
| void SetIsDefault(bool is_default); |
| bool GetIsDefault() const; |
| |
| // Sets/gets whether or not this view should be marked as "enabled" for the |
| // purpose exposing this state in the accessibility tree. As a general rule, |
| // it is not advisable to mark a View as enabled in the accessibility tree, |
| // while the real View is actually disabled, because such a View will not |
| // respond to user actions. |
| virtual void SetIsEnabled(bool is_enabled); |
| bool GetIsEnabled() const; |
| |
| void SetTableRowCount(int row_count); |
| void SetTableColumnCount(int column_count); |
| void SetAriaTableRowCount(int row_count); |
| void SetAriaTableColumnCount(int column_count); |
| void ClearTableRowCount(); |
| void ClearTableColumnCount(); |
| void ClearAriaTableRowCount(); |
| void ClearAriaTableColumnCount(); |
| |
| void SetTableRowIndex(int row_index); |
| int GetTableRowIndex() const; |
| void SetTableCellRowIndex(int cell_index); |
| void SetTableCellColumnIndex(int cell_index); |
| |
| void SetTableCellRowSpan(int row_span); |
| void SetTableCellColumnSpan(int column_span); |
| |
| void SetSortDirection(ax::mojom::SortDirection sort_direction); |
| |
| void ClearDescriptionAndDescriptionFrom(); |
| void RemoveDescription(); |
| |
| void SetDescription(const std::string& description, |
| const ax::mojom::DescriptionFrom description_from = |
| ax::mojom::DescriptionFrom::kAriaDescription); |
| void SetDescription(const std::u16string& description, |
| const ax::mojom::DescriptionFrom description_from = |
| ax::mojom::DescriptionFrom::kAriaDescription); |
| void SetDescription(View& describing_view); |
| // This function cannot follow the established pattern and be named |
| // GetDescription() because of a function of the same name in |
| // AXPlatformNodeDelegate. ViewAXPlatformNodeDelegate extends both |
| // ViewAccessibility and AXPlatformNodeDelegate, which would lead to conflicts |
| // and confusion. |
| // TODO(crbug.com/325137417): Rename to GetDescription once the ViewsAX |
| // project is completed and we don't have ViewAXPlatformNodeDelegate anymore. |
| std::u16string GetCachedDescription() const; |
| |
| void OnTooltipTextChanged( |
| std::optional<std::u16string> old_tooltip_text = std::nullopt); |
| |
| void OnViewAddedToWidget(); |
| |
| void SetPlaceholder(const std::string& placeholder); |
| |
| void AddAction(ax::mojom::Action action); |
| |
| void SetCheckedState(ax::mojom::CheckedState checked_state); |
| ax::mojom::CheckedState GetCheckedState() const; |
| void RemoveCheckedState(); |
| |
| void SetKeyShortcuts(const std::string& key_shortcuts); |
| void RemoveKeyShortcuts(); |
| |
| void SetAccessKey(const std::string& access_key); |
| void RemoveAccessKey(); |
| |
| void SetChildTreeNodeAppId(const std::string& app_id); |
| void RemoveChildTreeNodeAppId(); |
| |
| // 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); |
| |
| // 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 SetNextFocus(Widget* widget); |
| void SetPreviousFocus(Widget* widget); |
| Widget* GetNextWindowFocus() const; |
| Widget* GetPreviousWindowFocus() const; |
| |
| virtual void SetShowContextMenu(bool show_context_menu); |
| |
| void SetContainerLiveStatus(const std::string& status); |
| void RemoveContainerLiveStatus(); |
| |
| // Sets the kValue attribute of the accessible object. |
| // In case of ProgressBar, if progressBarIndicator value is negative, |
| // then kValue attribute should not be set. |
| void SetValue(const std::string& value); |
| void SetValue(std::u16string_view value); |
| void RemoveValue(); |
| std::u16string GetValue() const; |
| |
| void SetDefaultActionVerb( |
| const ax::mojom::DefaultActionVerb default_action_verb); |
| ax::mojom::DefaultActionVerb GetDefaultActionVerb() const; |
| void RemoveDefaultActionVerb(); |
| |
| void SetAutoComplete(const std::string& autocomplete); |
| |
| void SetHierarchicalLevel(int hierarchical_level); |
| |
| void SetHasFocusableAncestorRecursive(bool ancestor_focusable); |
| |
| // Updates the focusable state of the `data_` object. |
| // The view is considered focusable if it is not set to never receive focus |
| // This function must be called whenever an attribute that can affect the |
| // focusable state changes |
| virtual void UpdateFocusableState(); |
| |
| // Updates has_focusable_ancestor_ and the view's ignored state, which depends |
| // on this variable. |
| void SetHasFocusableAncestor(bool ancestor_focusable); |
| |
| // Recursively updates the focusable and invisible states of the view and its |
| // children. If a parent is invisible, non-explicitly invisible views inherit |
| // invisibility, affecting their final state and focusability. |
| void UpdateInvisibleByInheritanceRecursive(const View* initial_view, |
| bool invisible_by_inheritance); |
| |
| // This updates some shared state for the view and all its descendants. |
| // Called when `view_` gets added as a child of another View. |
| void OnViewHasNewAncestor(const View* new_ancestor); |
| |
| // This should only ever be called on the RootView. |
| void SetRootViewIsReadyToNotifyEvents(); |
| |
| // Sets the displayed, top-level URL for the active document. This should |
| // generally correspond to what would be shown in the Omnibox. Setting the |
| // value should only be done on the `RootView`. |
| void SetRootViewURL(const std::string& url); |
| |
| // Updates the invisible state of the `data_` object. The view is considered |
| // invisible if it is not visible and its role is not kAlert, or if it is |
| // "invisible by inheritance", meaning one of its ancestors was set to be |
| // invisible. |
| virtual void UpdateInvisibleState(); |
| |
| bool should_be_invisible() const { return should_be_invisible_; } |
| |
| // Override the child tree id. |
| void SetChildTreeID(ui::AXTreeID tree_id); |
| ui::AXTreeID GetChildTreeID() const; |
| void RemoveChildTreeID(); |
| |
| void SetChildTreeScaleFactor(float scale_factor); |
| |
| // 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. AnnouncePolitely() will speak |
| // the given string. AnnounceAlert() may make a stronger attempt to be |
| // noticeable; the screen reader may say something like "Alert: hello" |
| // instead of just "hello", and may interrupt any existing text being spoken. |
| // However, the screen reader may also treat the two calls the same. |
| // AnnounceText() is a deprecated alias for AnnounceAlert(). |
| // TODO(crbug.com/40287811) - Migrate all callers of AnnounceText() to |
| // one of the other two methods. |
| virtual void AnnounceAlert(std::u16string_view text); |
| virtual void AnnouncePolitely(std::u16string_view text); |
| virtual void AnnounceText(std::u16string_view text); |
| |
| virtual ui::AXPlatformNodeId GetUniqueId() const; |
| |
| View* view() const { return view_; } |
| AXVirtualView* FocusedVirtualChild() const { return focused_virtual_child_; } |
| |
| virtual AtomicViewAXTreeManager* GetAtomicViewAXTreeManagerForTesting() 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. |
| std::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 is_invisible_by_inheritance() const { |
| return is_invisible_by_inheritance_; |
| } |
| |
| bool has_focusable_ancestor() const { return has_focusable_ancestor_; } |
| |
| bool propagate_focus_to_ancestor() { return propagate_focus_to_ancestor_; } |
| |
| // If true, ensures an AtomicViewAXTreeManager is created for this view. |
| void set_needs_ax_tree_manager(bool value) { needs_ax_tree_manager_ = value; } |
| |
| bool needs_ax_tree_manager() { return needs_ax_tree_manager_; } |
| |
| // Used for testing. Allows a test to watch accessibility events. |
| const AccessibilityEventsCallback& accessibility_events_callback() const; |
| void set_accessibility_events_callback(AccessibilityEventsCallback callback); |
| |
| // WidgetObserver overrides. |
| void OnWidgetClosing(Widget* widget) override; |
| void OnWidgetDestroyed(Widget* widget) override; |
| |
| virtual void OnWidgetUpdated(Widget* widget, Widget* old_widget); |
| |
| void CompleteCacheInitialization(); |
| |
| bool IsAccessibilityEnabled() const; |
| |
| bool IsReadyToNotifyEvents() const { return ready_to_notify_events_; } |
| |
| bool is_initialized() const { |
| return initialization_state_ == State::kInitialized; |
| } |
| |
| // This mechanism allows views to listen for changes in the accessibility |
| // properties of other views. It facilitates communication between views that |
| // depend on each other's accessibility attributes, ensuring they can respond |
| // to updates effectively. For examples of how to do this, see |
| // view_accessibility_unittest.cc. |
| // Here's an example: |
| // |
| // class MyLabel : public View { |
| // public: |
| // void SetAccessibleName(const std::string& name) { |
| // GetViewAccessibility().SetName(name); |
| // } |
| // }; |
| // |
| // class MyButton : public View { |
| // public: |
| // explicit MyButton(MyLabel* label) { |
| // // Subscribe to label's accessible name changes. |
| // name_changed_subscription_ = |
| // label->GetViewAccessibility().AddStringAttributeChangedCallback( |
| // ax::mojom::StringAttribute::kName, |
| // base::BindRepeating(&MyButton::OnLabelNameChanged, |
| // base::Unretained(this))); |
| // } |
| // |
| // private: |
| // void OnLabelNameChanged(ax::mojom::StringAttribute attribute, |
| // const std::optional<std::string>& name) { |
| // // The call to SetName()/RemoveName() below will trigger an |
| // // accessibility event. In most cases, this would be the expected |
| // // behavior. However, if we don't want to trigger another one, we can |
| // // block the event temporarily by uncommenting the following line. |
| // // ScopedAccessibilityEventBlocker blocker(GetViewAccessibility()); |
| // if (name.has_value()) { |
| // GetViewAccessibility().SetName(name.value()); |
| // } else { |
| // GetViewAccessibility().RemoveName(); |
| // } |
| // } |
| // |
| // base::CallbackListSubscription name_changed_subscription_; |
| // }; |
| base::CallbackListSubscription AddRoleChangedCallback( |
| RoleCallbackList::CallbackType callback); |
| base::CallbackListSubscription AddIntAttributeChangedCallback( |
| ax::mojom::IntAttribute attribute, |
| IntAttributeCallbackList::CallbackType callback); |
| base::CallbackListSubscription AddStringAttributeChangedCallback( |
| ax::mojom::StringAttribute attribute, |
| StringAttributeCallbackList::CallbackType callback); |
| base::CallbackListSubscription AddBoolAttributeChangedCallback( |
| ax::mojom::BoolAttribute attribute, |
| BoolAttributeCallbackList::CallbackType callback); |
| base::CallbackListSubscription AddStateChangedCallback( |
| ax::mojom::State state, |
| StateCallbackList::CallbackType callback); |
| base::CallbackListSubscription AddIntListAttributeChangedCallback( |
| ax::mojom::IntListAttribute attribute, |
| IntListAttributeCallbackList::CallbackType callback); |
| |
| protected: |
| explicit ViewAccessibility(View* view); |
| |
| virtual void FireNativeEvent(ax::mojom::Event event_type); |
| |
| const ui::AXNodeData& data() const { return data_; } |
| |
| // Updates the ignored state of the `data_` object. |
| // The view is considered ignored if it should be ignored as per |
| // `should_be_ignored_`, or if it has been pruned (`pruned_`), or if its role |
| // is 'kNone'. |
| virtual void UpdateIgnoredState(); |
| |
| void SetState(ax::mojom::State state, bool is_enabled); |
| |
| // We don't want to fire accessibility events when the view is being |
| // initialized and any setters are called from their respective constructors. |
| // We only want to fire events of any subtree of views when that subtree of |
| // views is connected to a RootView. This way we ensure that we don't fire |
| // events for views that are not connected to a valid tree. See |
| // `SetRootViewIsReadyToNotifyEvents`. |
| virtual void UpdateReadyToNotifyEvents(); |
| |
| void SetReadyToNotifyEvents(); |
| |
| void SetWidgetClosedRecursive(Widget* widget, bool value); |
| |
| void SetDataForClosedWidget(ui::AXNodeData* data) const; |
| |
| // Contains data that is populated by the setters in this class. |
| // This member is tied to the ViewsAX project. Which is introducing a new |
| // system to set accessible properties in a "push" fashion (instead of pull). |
| // Authors are encouraged to start using it today, and it will eventually |
| // replace the old system. For now, while the migration to the new system |
| // happens, we allow the old system to coexist with he new one by just |
| // unioning the data from both systems. This is done in |
| // GetAccessibleNodeData(). |
| ui::AXNodeData data_; |
| |
| // Used to determine if a View should be ignored by accessibility clients by |
| // being a non-focusable child of a focusable ancestor. |
| // E.g., LabelButtons contain Labels, but a11y should just show that there's a |
| // button. This helps us make sure this element is excluded from the a11y tree |
| // if there's a focusable parent. All focusable elements should be leaf nodes. |
| // Exceptions to this rule will themselves be accessibility focusable. |
| // TODO(crbug.com/371237539): Eventually this should be standardized across |
| // platforms. |
| bool has_focusable_ancestor_ = false; |
| |
| bool pruned_ = false; |
| |
| // This keeps track of whether the view is invisible by an ancestor being set |
| // to be invisible. |
| bool is_invisible_by_inheritance_ = false; |
| |
| // This is set to true when the view is explicitly marked as ignored by |
| // `SetIsIgnored`. It is not the only condition that will cause a view to have |
| // the ignored accessible state, as `pruned_` and `is_leaf_` can also cause |
| // this. See `UpdateIgnoredState`. |
| bool should_be_ignored_ = false; |
| |
| // This is set to true when the view is explicitly marked as invisible by |
| // `SetIsInvisible`. It is not the only condition that will cause a view to |
| // have the invisible accessible state. See `UpdateInvisibleState`. |
| bool should_be_invisible_ = false; |
| |
| // Prevents accessibility events from being fired during initialization of |
| // the owning View. |
| // True once a View is connected to a RootView. |
| bool ready_to_notify_events_ = false; |
| |
| // Used for testing. Called every time an accessibility event is fired. |
| AccessibilityEventsCallback accessibility_events_callback_; |
| |
| bool ignore_missing_widget_for_testing_ = false; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(ViewTest, ViewAccessibilityReadyToNotifyEvents); |
| FRIEND_TEST_ALL_PREFIXES(ViewTest, |
| WidgetObserverViewWidgetClosedViewReparented); |
| friend class ScopedAccessibilityEventBlocker; |
| |
| // Fully initialize the cache. |
| void CompleteCacheInitializationRecursive(); |
| |
| void OnWidgetUpdatedRecursive(Widget* widget, Widget* old_widget); |
| |
| // Prune/Unprune all descendant views from the accessibility tree. We prune |
| // for two reasons: 1) The view has been explicitly marked as a leaf node, 2) |
| // The view is focusable and lacks focusable descendants (e.g. a button with a |
| // label and/or an image). |
| void PruneSubtree(); |
| void UnpruneSubtree(); |
| |
| void OnRoleChanged(ax::mojom::Role role); |
| void OnIntAttributeChanged(ax::mojom::IntAttribute attribute, |
| std::optional<int> value); |
| void OnStringAttributeChanged(ax::mojom::StringAttribute attribute, |
| const std::optional<std::string>& value); |
| void OnBoolAttributeChanged(ax::mojom::BoolAttribute attribute, |
| std::optional<bool> value); |
| void OnStateChanged(ax::mojom::State state, bool is_enabled); |
| void OnIntListAttributeChanged(ax::mojom::IntListAttribute attribute, |
| const std::optional<std::vector<int>>& value); |
| |
| void SetBlockNotifyEvents(bool block); |
| |
| ui::AXAttributeChangedCallbacks* GetOrCreateAXAttributeChangedCallbacks(); |
| |
| virtual void NotifyDataChanged(); |
| |
| // 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_{ui::AXUniqueId::Create()}; |
| |
| |
| // If set to true, anything that is a descendant of this view will be hidden |
| // from accessibility by 'pruning' it from the tree, and setting `pruned_` to |
| // true. |
| bool is_leaf_ = false; |
| |
| // 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; |
| |
| // Whether to move accessibility focus to an ancestor. |
| bool propagate_focus_to_ancestor_ = false; |
| |
| // Whether we need to ensure an AtomicViewAXTreeManager is created for this |
| // View. |
| bool needs_ax_tree_manager_ = false; |
| |
| bool is_widget_closed_ = false; |
| |
| std::unique_ptr<ui::AXAttributeChangedCallbacks> |
| attribute_changed_callbacks_ = nullptr; |
| |
| State initialization_state_ = State::kUninitialized; |
| |
| base::ScopedObservation<Widget, WidgetObserver> observation_{this}; |
| }; |
| |
| class IgnoreMissingWidgetForTestingScopedSetter { |
| public: |
| explicit IgnoreMissingWidgetForTestingScopedSetter( |
| ViewAccessibility& view_accessibility) |
| : view_accessibility_(&view_accessibility) { |
| view_accessibility_->SetIgnoreMissingWidgetForTesting(true); |
| } |
| ~IgnoreMissingWidgetForTestingScopedSetter() { |
| view_accessibility_->SetIgnoreMissingWidgetForTesting(false); |
| } |
| |
| private: |
| raw_ptr<ViewAccessibility> view_accessibility_; |
| }; |
| |
| class ScopedAccessibilityEventBlocker { |
| public: |
| explicit ScopedAccessibilityEventBlocker( |
| ViewAccessibility& view_accessibility) |
| : view_accessibility_(&view_accessibility) { |
| CHECK(view_accessibility_); |
| view_accessibility_->SetBlockNotifyEvents(true); |
| } |
| ~ScopedAccessibilityEventBlocker() { |
| CHECK(view_accessibility_); |
| view_accessibility_->SetBlockNotifyEvents(false); |
| } |
| |
| private: |
| raw_ptr<ViewAccessibility> view_accessibility_; |
| }; |
| |
| } // namespace views |
| |
| #endif // UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_H_ |