blob: 1d58988a789f662b5ce8f0ed2791c32ea83e1214 [file] [log] [blame]
// Copyright 2017 The Chromium 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 UI_ACCESSIBILITY_AX_EVENT_GENERATOR_H_
#define UI_ACCESSIBILITY_AX_EVENT_GENERATOR_H_
#include <bitset>
#include <map>
#include <ostream>
#include <set>
#include <vector>
#include "base/scoped_observer.h"
#include "ui/accessibility/ax_event_intent.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_observer.h"
namespace ui {
// Subclass of AXTreeObserver that automatically generates AXEvents to fire
// based on changes to an accessibility tree. Every platform
// tends to want different events, so this class lets each platform
// handle the events it wants and ignore the others.
class AX_EXPORT AXEventGenerator : public AXTreeObserver {
public:
enum class Event : int32_t {
ACCESS_KEY_CHANGED,
ACTIVE_DESCENDANT_CHANGED,
ALERT,
// ATK treats alignment, indentation, and other format-related attributes as
// text attributes even when they are only applicable to the entire object.
// And it lacks an event for use when object attributes have changed.
ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED,
ATOMIC_CHANGED,
AUTO_COMPLETE_CHANGED,
BUSY_CHANGED,
CHECKED_STATE_CHANGED,
CHILDREN_CHANGED,
CLASS_NAME_CHANGED,
COLLAPSED,
CONTROLS_CHANGED,
DESCRIBED_BY_CHANGED,
DESCRIPTION_CHANGED,
DOCUMENT_SELECTION_CHANGED,
DOCUMENT_TITLE_CHANGED,
DROPEFFECT_CHANGED,
// TODO(nektar): Deprecate this event and replace it with
// "VALUE_IN_TEXT_FIELD_CHANGED".
EDITABLE_TEXT_CHANGED,
ENABLED_CHANGED,
EXPANDED,
FOCUS_CHANGED,
FLOW_FROM_CHANGED,
FLOW_TO_CHANGED,
GRABBED_CHANGED,
HASPOPUP_CHANGED,
HIERARCHICAL_LEVEL_CHANGED,
IGNORED_CHANGED,
IMAGE_ANNOTATION_CHANGED,
INVALID_STATUS_CHANGED,
KEY_SHORTCUTS_CHANGED,
LABELED_BY_CHANGED,
LANGUAGE_CHANGED,
LAYOUT_INVALIDATED, // Fired when aria-busy turns from true to false.
LIVE_REGION_CHANGED, // Fired on the root of a live region.
LIVE_REGION_CREATED,
LIVE_REGION_NODE_CHANGED, // Fired on a node within a live region.
LIVE_RELEVANT_CHANGED,
LIVE_STATUS_CHANGED,
LOAD_COMPLETE,
LOAD_START,
MENU_ITEM_SELECTED,
MULTILINE_STATE_CHANGED,
MULTISELECTABLE_STATE_CHANGED,
NAME_CHANGED,
OBJECT_ATTRIBUTE_CHANGED,
OTHER_ATTRIBUTE_CHANGED,
PARENT_CHANGED,
PLACEHOLDER_CHANGED,
PORTAL_ACTIVATED,
POSITION_IN_SET_CHANGED,
RANGE_VALUE_CHANGED,
RANGE_VALUE_MAX_CHANGED,
RANGE_VALUE_MIN_CHANGED,
RANGE_VALUE_STEP_CHANGED,
READONLY_CHANGED,
RELATED_NODE_CHANGED,
REQUIRED_STATE_CHANGED,
ROLE_CHANGED,
ROW_COUNT_CHANGED,
SCROLL_HORIZONTAL_POSITION_CHANGED,
SCROLL_VERTICAL_POSITION_CHANGED,
SELECTED_CHANGED,
SELECTED_CHILDREN_CHANGED,
SELECTED_VALUE_CHANGED,
SELECTION_IN_TEXT_FIELD_CHANGED,
SET_SIZE_CHANGED,
SORT_CHANGED,
STATE_CHANGED,
SUBTREE_CREATED,
TEXT_ATTRIBUTE_CHANGED,
VALUE_IN_TEXT_FIELD_CHANGED,
// This event is for the exact set of attributes that affect
// the MSAA/IAccessible state on Windows. Not needed on other platforms,
// but very natural to compute here.
WIN_IACCESSIBLE_STATE_CHANGED,
MAX_VALUE = WIN_IACCESSIBLE_STATE_CHANGED,
};
// For distinguishing between show and hide state when a node has
// IGNORED_CHANGED event.
enum class IgnoredChangedState : uint8_t { kShow, kHide, kCount = 2 };
struct AX_EXPORT EventParams {
EventParams(Event event,
ax::mojom::EventFrom event_from,
const std::vector<AXEventIntent>& event_intents);
EventParams(const EventParams& other);
~EventParams();
bool operator==(const EventParams& rhs) const;
bool operator<(const EventParams& rhs) const;
Event event;
ax::mojom::EventFrom event_from;
std::vector<AXEventIntent> event_intents;
};
struct TargetedEvent final {
// |node| must not be null
TargetedEvent(ui::AXNode* node, const EventParams& event_params);
~TargetedEvent() = default;
ui::AXNode* node;
const EventParams& event_params;
};
class AX_EXPORT Iterator
: public std::iterator<std::input_iterator_tag, TargetedEvent> {
public:
Iterator(
const std::map<AXNode*, std::set<EventParams>>& map,
const std::map<AXNode*, std::set<EventParams>>::const_iterator& head);
Iterator(const Iterator& other);
~Iterator();
bool operator!=(const Iterator& rhs) const;
Iterator& operator++();
TargetedEvent operator*() const;
private:
const std::map<AXNode*, std::set<EventParams>>& map_;
std::map<AXNode*, std::set<EventParams>>::const_iterator map_iter_;
std::set<EventParams>::const_iterator set_iter_;
};
// For storing ignored changed states for a particular node. We use bitset as
// the underlying data structure to improve memory usage.
// We use the index of AXEventGenerator::IgnoredChangedState enum
// to access the bitset data.
// e.g. AXEventGenerator::IgnoredChangedState::kShow has index 0 in the
// IgnoredChangedState enum. If |IgnoredChangedStatesBitset[0]| is set, it
// means IgnoredChangedState::kShow is present. Similarly, kHide has index 1
// in the enum, and it corresponds to |IgnoredChangedStatesBitset[1]|.
using IgnoredChangedStatesBitset =
std::bitset<static_cast<size_t>(IgnoredChangedState::kCount)>;
using const_iterator = Iterator;
using iterator = Iterator;
using value_type = TargetedEvent;
// If you use this constructor, you must call SetTree
// before using this class.
AXEventGenerator();
// Automatically registers itself as the observer of |tree| and
// clears it on desctruction. |tree| must be valid for the lifetime
// of this object.
explicit AXEventGenerator(AXTree* tree);
~AXEventGenerator() override;
// Clears this class as the observer of the previous tree that was
// being monitored, if any, and starts monitoring |new_tree|, if not
// nullptr. Note that |new_tree| must be valid for the lifetime of
// this object or until you call SetTree again.
void SetTree(AXTree* new_tree);
// Null |tree_| without accessing it or destroying it.
void ReleaseTree();
//
// Methods that make this class behave like an STL container, which simplifies
// the process of iterating through generated events.
//
bool empty() const;
size_t size() const;
Iterator begin() const;
Iterator end() const;
// Clear any previously added events.
void ClearEvents();
// This is called automatically based on changes to the tree observed
// by AXTreeObserver, but you can also call it directly to add events
// and retrieve them later.
//
// Note that events are organized by node and then by event id to
// efficiently remove duplicates, so events won't be retrieved in the
// same order they were added.
void AddEvent(ui::AXNode* node, Event event);
void set_always_fire_load_complete(bool val) {
always_fire_load_complete_ = val;
}
void AddEventsForTesting(AXNode* node, const std::set<EventParams>& events);
protected:
// AXTreeObserver overrides.
void OnNodeDataChanged(AXTree* tree,
const AXNodeData& old_node_data,
const AXNodeData& new_node_data) override;
void OnRoleChanged(AXTree* tree,
AXNode* node,
ax::mojom::Role old_role,
ax::mojom::Role new_role) override;
void OnStateChanged(AXTree* tree,
AXNode* node,
ax::mojom::State state,
bool new_value) override;
void OnStringAttributeChanged(AXTree* tree,
AXNode* node,
ax::mojom::StringAttribute attr,
const std::string& old_value,
const std::string& new_value) override;
void OnIntAttributeChanged(AXTree* tree,
AXNode* node,
ax::mojom::IntAttribute attr,
int32_t old_value,
int32_t new_value) override;
void OnFloatAttributeChanged(AXTree* tree,
AXNode* node,
ax::mojom::FloatAttribute attr,
float old_value,
float new_value) override;
void OnBoolAttributeChanged(AXTree* tree,
AXNode* node,
ax::mojom::BoolAttribute attr,
bool new_value) override;
void OnIntListAttributeChanged(
AXTree* tree,
AXNode* node,
ax::mojom::IntListAttribute attr,
const std::vector<int32_t>& old_value,
const std::vector<int32_t>& new_value) override;
void OnTreeDataChanged(AXTree* tree,
const ui::AXTreeData& old_data,
const ui::AXTreeData& new_data) override;
void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override;
void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) override;
void OnNodeWillBeReparented(AXTree* tree, AXNode* node) override;
void OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) override;
void OnNodeReparented(AXTree* tree, AXNode* node) override;
void OnAtomicUpdateFinished(AXTree* tree,
bool root_changed,
const std::vector<Change>& changes) override;
private:
void FireLiveRegionEvents(AXNode* node);
void FireActiveDescendantEvents();
void FireValueInTextFieldChangedEvent(AXTree* tree, AXNode* target_node);
void FireRelationSourceEvents(AXTree* tree, AXNode* target_node);
bool ShouldFireLoadEvents(AXNode* node);
// Remove excessive events for a tree update containing node.
// We remove certain events on a node when it flips its IGNORED state to
// either show/hide and one of the node's ancestor has also flipped its
// IGNORED state in the same way (show/hide) in the tree update.
// |ancestor_has_ignored_map| contains if a node's ancestor has changed to
// IGNORED state.
// Map's key is an AXNode.
// Map's value is a std::bitset containing IgnoredChangedStates(kShow/kHide).
// - Map's value IgnoredChangedStatesBitset contains kShow if an ancestor
// of node removed its IGNORED state.
// - Map's value IgnoredChangedStatesBitset contains kHide if an ancestor
// of node changed to IGNORED state.
// - When IgnoredChangedStatesBitset is not set, it means neither the
// node nor its ancestor has IGNORED_CHANGED.
void TrimEventsDueToAncestorIgnoredChanged(
AXNode* node,
std::map<AXNode*, IgnoredChangedStatesBitset>&
ancestor_ignored_changed_map);
void PostprocessEvents();
static void GetRestrictionStates(ax::mojom::Restriction restriction,
bool* is_enabled,
bool* is_readonly);
// Returns a vector of values unique to either |lhs| or |rhs|
static std::vector<int32_t> ComputeIntListDifference(
const std::vector<int32_t>& lhs,
const std::vector<int32_t>& rhs);
AXTree* tree_ = nullptr; // Not owned.
std::map<AXNode*, std::set<EventParams>> tree_events_;
// Valid between the call to OnIntAttributeChanged and the call to
// OnAtomicUpdateFinished. List of nodes whose active descendant changed.
std::vector<AXNode*> active_descendant_changed_;
bool always_fire_load_complete_ = false;
// Please make sure that this ScopedObserver is always declared last in order
// to prevent any use-after-free.
ScopedObserver<AXTree, AXTreeObserver> tree_event_observer_{this};
};
AX_EXPORT std::ostream& operator<<(std::ostream& os,
AXEventGenerator::Event event);
AX_EXPORT const char* ToString(AXEventGenerator::Event event);
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_EVENT_GENERATOR_H_