Commit 5755004e authored by Kurt Catti-Schmidt (SCHMIDT)'s avatar Kurt Catti-Schmidt (SCHMIDT) Committed by Chromium LUCI CQ

Adding ViewsAXTreeManager output to chrome://accessibility

This change creates a ViewsAXTreeManager for each top-level Widget
behind the runtime flag "--enable-features=AccessibilityTreeForViews".
This change also displays the contents of the entire tree built by
ViewsAXTreeManager under chrome://accessibility under a new "Top
Level Windows" section that is only visible when the runtime flag
is enabled.

Much of the code changes here are converting methods that serialize
BrowserAccessibility nodes to instead take AXNode's. This was fairly
straightforward for most of these, however
AccessibilityTreeFormatterBlink::AddProperties had to be forked due to
all of the properties that depend on the delegate.

WidgetAXTreeIDMap is added to map between Widget objects and their
root AXTreeID.

In order to suppress build errors on ChromeOS and macOS, this change is
limited to non-ChromeOS Aura builds (Windows and Linux).

AX-Relnotes: Displays internal UI tree in chrome://accessibility.

Bug: 1049261
Change-Id: Id3918e177df360b284d186638a04d8be494508c7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2402398
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarAllen Bauer <kylixrd@chromium.org>
Reviewed-by: default avatarNektarios Paisios <nektar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836275}
parent 37a2f4f0
...@@ -65,8 +65,14 @@ class AccessibilityUIMessageHandler : public content::WebUIMessageHandler { ...@@ -65,8 +65,14 @@ class AccessibilityUIMessageHandler : public content::WebUIMessageHandler {
void ToggleAccessibility(const base::ListValue* args); void ToggleAccessibility(const base::ListValue* args);
void SetGlobalFlag(const base::ListValue* args); void SetGlobalFlag(const base::ListValue* args);
void GetRequestTypeAndFilters(const base::DictionaryValue* data,
std::string& request_type,
std::string& allow,
std::string& allow_empty,
std::string& deny);
void RequestWebContentsTree(const base::ListValue* args); void RequestWebContentsTree(const base::ListValue* args);
void RequestNativeUITree(const base::ListValue* args); void RequestNativeUITree(const base::ListValue* args);
void RequestWidgetsTree(const base::ListValue* args);
void RequestAccessibilityEvents(const base::ListValue* args); void RequestAccessibilityEvents(const base::ListValue* args);
void Callback(const std::string&); void Callback(const std::string&);
void StopRecording(content::WebContents* web_contents); void StopRecording(content::WebContents* web_contents);
......
...@@ -9,13 +9,13 @@ body { ...@@ -9,13 +9,13 @@ body {
font-size: 12px; font-size: 12px;
margin: 10px 20px; margin: 10px 20px;
min-width: 47em; min-width: 47em;
padding-bottom: 65px; padding-block-end: 65px;
} }
img { img {
float: left; float: left;
height: 16px; height: 16px;
padding-right: 5px; padding-inline-end: 5px;
width: 16px; width: 16px;
} }
...@@ -57,10 +57,14 @@ p { ...@@ -57,10 +57,14 @@ p {
margin: 0 0 12px 32px; margin: 0 0 12px 32px;
} }
.hidden {
display: none;
}
a { a {
margin-top: 10px; margin-block-end: 10px;
margin-bottom: 10px; margin-block-start: 10px;
margin-left: 20px; margin-inline-start: 20px;
} }
label { label {
...@@ -74,5 +78,5 @@ label.disabled { ...@@ -74,5 +78,5 @@ label.disabled {
} }
label input[type='checkbox'] { label input[type='checkbox'] {
margin-right: 6px; margin-inline-end: 6px;
} }
...@@ -195,5 +195,11 @@ found in the LICENSE file. ...@@ -195,5 +195,11 @@ found in the LICENSE file.
<h2>Pages:</h2> <h2>Pages:</h2>
<div id="pages" class="list"></div> <div id="pages" class="list"></div>
<h2 id="widgets-header">Top Level Windows:</h2>
<p id="widgets-not-supported" class="hidden">
Top Level Window support is not available on this platform.
</p>
<div id="widgets" class="list"></div>
</body> </body>
</html> </html>
...@@ -32,6 +32,12 @@ class BrowserProxy { ...@@ -32,6 +32,12 @@ class BrowserProxy {
]); ]);
} }
requestWidgetsTree(widgetId, requestType, allow, allowEmpty, deny) {
chrome.send(
'requestWidgetsTree',
[{widgetId, requestType, filters: {allow, allowEmpty, deny}}]);
}
requestAccessibilityEvents(processId, routingId, start) { requestAccessibilityEvents(processId, routingId, start) {
chrome.send('requestAccessibilityEvents', [{processId, routingId, start}]); chrome.send('requestAccessibilityEvents', [{processId, routingId, start}]);
} }
...@@ -81,6 +87,8 @@ function getIdFromData(data) { ...@@ -81,6 +87,8 @@ function getIdFromData(data) {
return data.processId + '.' + data.routingId; return data.processId + '.' + data.routingId;
} else if (data.type == 'browser') { } else if (data.type == 'browser') {
return 'browser.' + data.sessionId; return 'browser.' + data.sessionId;
} else if (data.type == 'widget') {
return 'widget.' + data.widgetId;
} else { } else {
console.error('Unknown data type.', data); console.error('Unknown data type.', data);
return ''; return '';
...@@ -119,6 +127,9 @@ function requestTree(data, element) { ...@@ -119,6 +127,9 @@ function requestTree(data, element) {
browserProxy.requestNativeUITree( browserProxy.requestNativeUITree(
data.sessionId, requestType, allow, allowEmpty, deny); data.sessionId, requestType, allow, allowEmpty, deny);
}, delay); }, delay);
} else if (data.type == 'widget') {
browserProxy.requestWidgetsTree(
data.widgetId, requestType, allow, allowEmpty, deny);
} else { } else {
browserProxy.requestWebContentsTree( browserProxy.requestWebContentsTree(
data.processId, data.routingId, requestType, allow, allowEmpty, deny); data.processId, data.routingId, requestType, allow, allowEmpty, deny);
...@@ -179,6 +190,25 @@ function initialize() { ...@@ -179,6 +190,25 @@ function initialize() {
addToBrowsersList(browsers[i]); addToBrowsersList(browsers[i]);
} }
if (data['viewsAccessibility']) {
const widgets = data['widgets'];
if (widgets.length === 0) {
// There should always be at least 1 Widget displayed (for the current
// window). If this is not the case, and Views Accessibility is enabled,
// the only possibility is that Views Accessibility is not enabled for
// the current platform. Display a message to the user to indicate this.
$('widgets-not-supported').style.display = 'block';
} else {
for (let i = 0; i < widgets.length; i++) {
addToWidgetsList(widgets[i]);
}
}
} else {
$('widgets').style.display = 'none';
$('widgets-header').style.display = 'none';
}
// Cache filters so they're easily accessible on page refresh. // Cache filters so they're easily accessible on page refresh.
const allow = window.localStorage['chrome-accessibility-filter-allow']; const allow = window.localStorage['chrome-accessibility-filter-allow'];
const allowEmpty = const allowEmpty =
...@@ -233,6 +263,17 @@ function addToBrowsersList(data) { ...@@ -233,6 +263,17 @@ function addToBrowsersList(data) {
browsers.appendChild(row); browsers.appendChild(row);
} }
function addToWidgetsList(data) {
const id = getIdFromData(data);
const row = document.createElement('div');
row.className = 'row';
row.id = id;
formatRow(row, data, null);
const widgets = $('widgets');
widgets.appendChild(row);
}
function formatRow(row, data, requestType) { function formatRow(row, data, requestType) {
if (!('url' in data)) { if (!('url' in data)) {
if ('error' in data) { if ('error' in data) {
...@@ -528,4 +569,4 @@ function createAccessibilityOutputElement(data, id, type) { ...@@ -528,4 +569,4 @@ function createAccessibilityOutputElement(data, id, type) {
return treeElement; return treeElement;
} }
document.addEventListener('DOMContentLoaded', initialize); document.addEventListener('DOMContentLoaded', initialize);
\ No newline at end of file
...@@ -24,6 +24,10 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink ...@@ -24,6 +24,10 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink
base::Value BuildTreeForWindow(gfx::AcceleratedWidget widget) const override; base::Value BuildTreeForWindow(gfx::AcceleratedWidget widget) const override;
base::Value BuildTreeForSelector( base::Value BuildTreeForSelector(
const AXTreeSelector& selector) const override; const AXTreeSelector& selector) const override;
base::Value BuildTreeForNode(ui::AXNode* node) const override;
std::string DumpInternalAccessibilityTree(
ui::AXTreeID tree_id,
const std::vector<AXPropertyFilter>& property_filters) override;
protected: protected:
void AddDefaultFilters( void AddDefaultFilters(
...@@ -33,6 +37,8 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink ...@@ -33,6 +37,8 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink
void RecursiveBuildTree(const BrowserAccessibility& node, void RecursiveBuildTree(const BrowserAccessibility& node,
base::Value* dict) const; base::Value* dict) const;
void RecursiveBuildTree(const ui::AXNode& node, base::Value* dict) const;
uint32_t ChildCount(const BrowserAccessibility& node) const; uint32_t ChildCount(const BrowserAccessibility& node) const;
BrowserAccessibility* GetChild(const BrowserAccessibility& node, BrowserAccessibility* GetChild(const BrowserAccessibility& node,
uint32_t i) const; uint32_t i) const;
...@@ -40,6 +46,8 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink ...@@ -40,6 +46,8 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink
void AddProperties(const BrowserAccessibility& node, void AddProperties(const BrowserAccessibility& node,
base::DictionaryValue* dict) const; base::DictionaryValue* dict) const;
void AddProperties(const ui::AXNode& node, base::DictionaryValue* dict) const;
std::string ProcessTreeForOutput( std::string ProcessTreeForOutput(
const base::DictionaryValue& node) const override; const base::DictionaryValue& node) const override;
}; };
......
...@@ -13,6 +13,7 @@ AXTreeManagerMap::AXTreeManagerMap() {} ...@@ -13,6 +13,7 @@ AXTreeManagerMap::AXTreeManagerMap() {}
AXTreeManagerMap::~AXTreeManagerMap() {} AXTreeManagerMap::~AXTreeManagerMap() {}
// static
AXTreeManagerMap& AXTreeManagerMap::GetInstance() { AXTreeManagerMap& AXTreeManagerMap::GetInstance() {
static base::NoDestructor<AXTreeManagerMap> instance; static base::NoDestructor<AXTreeManagerMap> instance;
return *instance; return *instance;
......
...@@ -16,6 +16,8 @@ class Value; ...@@ -16,6 +16,8 @@ class Value;
namespace ui { namespace ui {
class AXNode;
class AXTreeID;
class AXPlatformNodeDelegate; class AXPlatformNodeDelegate;
// A utility class for formatting platform-specific accessibility information, // A utility class for formatting platform-specific accessibility information,
...@@ -75,6 +77,14 @@ class AX_EXPORT AXTreeFormatter { ...@@ -75,6 +77,14 @@ class AX_EXPORT AXTreeFormatter {
// given pattern. // given pattern.
virtual base::Value BuildTreeForSelector(const AXTreeSelector&) const = 0; virtual base::Value BuildTreeForSelector(const AXTreeSelector&) const = 0;
// Build an accessibility tree for an application with |node| as the root.
virtual base::Value BuildTreeForNode(ui::AXNode* node) const = 0;
// Returns a string representing the internal tree represented by |tree_id|.
virtual std::string DumpInternalAccessibilityTree(
ui::AXTreeID tree_id,
const std::vector<AXPropertyFilter>& property_filters) = 0;
// Dumps accessibility tree. // Dumps accessibility tree.
virtual std::string FormatTree(const base::Value& tree_node) const = 0; virtual std::string FormatTree(const base::Value& tree_node) const = 0;
......
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
#include "ui/accessibility/platform/inspect/ax_tree_formatter_base.h" #include "ui/accessibility/platform/inspect/ax_tree_formatter_base.h"
#include "base/notreached.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/accessibility/platform/inspect/ax_property_node.h" #include "ui/accessibility/platform/inspect/ax_property_node.h"
namespace ui { namespace ui {
...@@ -51,6 +53,12 @@ std::string AXTreeFormatterBase::FormatTree(const base::Value& dict) const { ...@@ -51,6 +53,12 @@ std::string AXTreeFormatterBase::FormatTree(const base::Value& dict) const {
return contents; return contents;
} }
base::Value AXTreeFormatterBase::BuildTreeForNode(ui::AXNode* root) const {
NOTREACHED()
<< "Only supported when called on AccessibilityTreeFormatterBlink.";
return base::Value();
}
void AXTreeFormatterBase::RecursiveFormatTree(const base::Value& dict, void AXTreeFormatterBase::RecursiveFormatTree(const base::Value& dict,
std::string* contents, std::string* contents,
int depth) const { int depth) const {
...@@ -103,6 +111,14 @@ void AXTreeFormatterBase::set_show_ids(bool show_ids) { ...@@ -103,6 +111,14 @@ void AXTreeFormatterBase::set_show_ids(bool show_ids) {
show_ids_ = show_ids; show_ids_ = show_ids;
} }
std::string AXTreeFormatterBase::DumpInternalAccessibilityTree(
ui::AXTreeID tree_id,
const std::vector<AXPropertyFilter>& property_filters) {
NOTREACHED()
<< "Only supported when called on AccessibilityTreeFormatterBlink.";
return std::string("");
}
std::vector<AXPropertyNode> AXTreeFormatterBase::PropertyFilterNodesFor( std::vector<AXPropertyNode> AXTreeFormatterBase::PropertyFilterNodesFor(
const std::string& line_index) const { const std::string& line_index) const {
std::vector<AXPropertyNode> list; std::vector<AXPropertyNode> list;
......
...@@ -31,10 +31,14 @@ class AX_EXPORT AXTreeFormatterBase : public AXTreeFormatter { ...@@ -31,10 +31,14 @@ class AX_EXPORT AXTreeFormatterBase : public AXTreeFormatter {
// AXTreeFormatter overrides. // AXTreeFormatter overrides.
std::string FormatTree(const base::Value& tree_node) const override; std::string FormatTree(const base::Value& tree_node) const override;
base::Value BuildTreeForNode(ui::AXNode* root) const override;
void SetPropertyFilters(const std::vector<AXPropertyFilter>& property_filters, void SetPropertyFilters(const std::vector<AXPropertyFilter>& property_filters,
PropertyFilterSet default_filters_set) override; PropertyFilterSet default_filters_set) override;
void SetNodeFilters(const std::vector<AXNodeFilter>& node_filters) override; void SetNodeFilters(const std::vector<AXNodeFilter>& node_filters) override;
void set_show_ids(bool show_ids) override; void set_show_ids(bool show_ids) override;
std::string DumpInternalAccessibilityTree(
ui::AXTreeID tree_id,
const std::vector<AXPropertyFilter>& property_filters) override;
protected: protected:
static const char kChildrenDictAttr[]; static const char kChildrenDictAttr[];
......
...@@ -62,6 +62,8 @@ component("views") { ...@@ -62,6 +62,8 @@ component("views") {
"accessibility/ax_virtual_view.h", "accessibility/ax_virtual_view.h",
"accessibility/view_accessibility.h", "accessibility/view_accessibility.h",
"accessibility/view_accessibility_utils.h", "accessibility/view_accessibility_utils.h",
"accessibility/views_ax_tree_manager.h",
"accessibility/widget_ax_tree_id_map.h",
"accessible_pane_view.h", "accessible_pane_view.h",
"animation/animation_delegate_views.h", "animation/animation_delegate_views.h",
"animation/bounds_animator.h", "animation/bounds_animator.h",
...@@ -731,6 +733,12 @@ component("views") { ...@@ -731,6 +733,12 @@ component("views") {
"corewm/tooltip_win.cc", "corewm/tooltip_win.cc",
] ]
} }
if (!is_chromeos) {
sources += [
"accessibility/views_ax_tree_manager.cc",
"accessibility/widget_ax_tree_id_map.cc",
]
}
deps += [ deps += [
"//ui/aura", "//ui/aura",
"//ui/events", "//ui/events",
...@@ -830,8 +838,6 @@ component("views") { ...@@ -830,8 +838,6 @@ component("views") {
if (use_aura) { if (use_aura) {
sources += [ sources += [
"accessibility/views_ax_tree_manager.cc",
"accessibility/views_ax_tree_manager.h",
"accessibility/views_utilities_aura.cc", "accessibility/views_utilities_aura.cc",
"accessibility/views_utilities_aura.h", "accessibility/views_utilities_aura.h",
] ]
......
...@@ -10,10 +10,14 @@ ...@@ -10,10 +10,14 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_tree_manager_map.h"
#include "ui/accessibility/platform/ax_platform_node.h" #include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h"
#include "ui/base/buildflags.h" #include "ui/base/buildflags.h"
#include "ui/views/accessibility/views_ax_tree_manager.h"
#include "ui/views/accessibility/widget_ax_tree_id_map.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/root_view.h" #include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
...@@ -57,7 +61,20 @@ ViewAccessibility::ViewAccessibility(View* view) ...@@ -57,7 +61,20 @@ ViewAccessibility::ViewAccessibility(View* view)
: view_(view), : view_(view),
focused_virtual_child_(nullptr), focused_virtual_child_(nullptr),
is_leaf_(false), is_leaf_(false),
is_ignored_(false) {} is_ignored_(false) {
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
if (features::IsAccessibilityTreeForViewsEnabled()) {
Widget* widget = view_->GetWidget();
if (widget && widget->is_top_level() &&
!WidgetAXTreeIDMap::GetInstance().HasWidget(widget)) {
View* root_view = static_cast<View*>(widget->GetRootView());
if (root_view && root_view == view) {
ax_tree_manager_ = std::make_unique<views::ViewsAXTreeManager>(widget);
}
}
}
#endif
}
ViewAccessibility::~ViewAccessibility() = default; ViewAccessibility::~ViewAccessibility() = default;
...@@ -139,6 +156,30 @@ bool ViewAccessibility::IsLeaf() const { ...@@ -139,6 +156,30 @@ bool ViewAccessibility::IsLeaf() const {
return is_leaf_; return is_leaf_;
} }
ViewsAXTreeManager* ViewAccessibility::AXTreeManager() const {
ViewsAXTreeManager* manager = nullptr;
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
Widget* widget = view_->GetWidget();
// Don't return managers for closing Widgets.
if (widget->IsClosed())
return nullptr;
manager = ax_tree_manager_.get();
// ViewsAXTreeManagers are only created for top-level windows (Widgets). For
// non top-level Views, look up the Widget's tree ID to retrieve the manager.
if (!manager) {
ui::AXTreeID tree_id =
WidgetAXTreeIDMap::GetInstance().GetWidgetTreeID(widget);
DCHECK_NE(tree_id, ui::AXTreeIDUnknown());
manager = static_cast<views::ViewsAXTreeManager*>(
ui::AXTreeManagerMap::GetInstance().GetManager(tree_id));
}
#endif
return manager;
}
bool ViewAccessibility::IsIgnored() const { bool ViewAccessibility::IsIgnored() const {
return is_ignored_; return is_ignored_;
} }
......
...@@ -28,6 +28,7 @@ class AXPlatformNodeDelegate; ...@@ -28,6 +28,7 @@ class AXPlatformNodeDelegate;
namespace views { namespace views {
class View; class View;
class ViewsAXTreeManager;
class Widget; class Widget;
// An object that manages the accessibility interface for a View. // An object that manages the accessibility interface for a View.
...@@ -117,6 +118,7 @@ class VIEWS_EXPORT ViewAccessibility { ...@@ -117,6 +118,7 @@ class VIEWS_EXPORT ViewAccessibility {
View* view() const { return view_; } View* view() const { return view_; }
AXVirtualView* FocusedVirtualChild() const { return focused_virtual_child_; } AXVirtualView* FocusedVirtualChild() const { return focused_virtual_child_; }
virtual bool IsLeaf() const; virtual bool IsLeaf() const;
ViewsAXTreeManager* AXTreeManager() const;
virtual bool IsIgnored() const; virtual bool IsIgnored() const;
// //
...@@ -219,6 +221,12 @@ class VIEWS_EXPORT ViewAccessibility { ...@@ -219,6 +221,12 @@ class VIEWS_EXPORT ViewAccessibility {
// screen readers, transition focus from one widget to another. // screen readers, transition focus from one widget to another.
Widget* next_focus_ = nullptr; Widget* next_focus_ = nullptr;
Widget* previous_focus_ = nullptr; Widget* previous_focus_ = nullptr;
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
// 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 } // namespace views
......
...@@ -12,12 +12,14 @@ ...@@ -12,12 +12,14 @@
#include "base/location.h" #include "base/location.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_tree_manager_map.h" #include "ui/accessibility/ax_tree_manager_map.h"
#include "ui/accessibility/ax_tree_source_checker.h" #include "ui/accessibility/ax_tree_source_checker.h"
#include "ui/accessibility/ax_tree_update.h" #include "ui/accessibility/ax_tree_update.h"
#include "ui/views/accessibility/ax_aura_obj_wrapper.h" #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
#include "ui/views/accessibility/widget_ax_tree_id_map.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
...@@ -25,14 +27,15 @@ namespace views { ...@@ -25,14 +27,15 @@ namespace views {
ViewsAXTreeManager::ViewsAXTreeManager(Widget* widget) ViewsAXTreeManager::ViewsAXTreeManager(Widget* widget)
: widget_(widget), : widget_(widget),
tree_source_(cache_.GetOrCreate(widget), tree_id_(ui::AXTreeID::CreateNewAXTreeID()),
ui::AXTreeID::CreateNewAXTreeID(), tree_source_(cache_.GetOrCreate(widget), tree_id_, &cache_),
&cache_),
tree_serializer_(&tree_source_), tree_serializer_(&tree_source_),
event_generator_(&ax_tree_) { event_generator_(&ax_tree_) {
DCHECK(widget); DCHECK(widget);
ui::AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); ui::AXTreeManagerMap::GetInstance().AddTreeManager(tree_id_, this);
views_event_observer_.Add(AXEventManager::Get()); views::WidgetAXTreeIDMap::GetInstance().AddWidget(tree_id_, widget);
views_event_observer_.Observe(AXEventManager::Get());
widget_observer_.Observe(widget);
View* root_view = widget->GetRootView(); View* root_view = widget->GetRootView();
if (root_view) if (root_view)
root_view->NotifyAccessibilityEvent(ax::mojom::Event::kLoadComplete, true); root_view->NotifyAccessibilityEvent(ax::mojom::Event::kLoadComplete, true);
...@@ -40,8 +43,9 @@ ViewsAXTreeManager::ViewsAXTreeManager(Widget* widget) ...@@ -40,8 +43,9 @@ ViewsAXTreeManager::ViewsAXTreeManager(Widget* widget)
ViewsAXTreeManager::~ViewsAXTreeManager() { ViewsAXTreeManager::~ViewsAXTreeManager() {
event_generator_.ReleaseTree(); event_generator_.ReleaseTree();
views_event_observer_.RemoveAll(); views_event_observer_.Reset();
ui::AXTreeManagerMap::GetInstance().RemoveTreeManager(GetTreeID()); widget_observer_.Reset();
ui::AXTreeManagerMap::GetInstance().RemoveTreeManager(tree_id_);
} }
void ViewsAXTreeManager::SetGeneratedEventCallbackForTesting( void ViewsAXTreeManager::SetGeneratedEventCallbackForTesting(
...@@ -56,6 +60,9 @@ void ViewsAXTreeManager::UnsetGeneratedEventCallbackForTesting() { ...@@ -56,6 +60,9 @@ void ViewsAXTreeManager::UnsetGeneratedEventCallbackForTesting() {
ui::AXNode* ViewsAXTreeManager::GetNodeFromTree( ui::AXNode* ViewsAXTreeManager::GetNodeFromTree(
const ui::AXTreeID tree_id, const ui::AXTreeID tree_id,
const ui::AXNode::AXID node_id) const { const ui::AXNode::AXID node_id) const {
if (!widget_)
return nullptr;
const ui::AXTreeManager* manager = const ui::AXTreeManager* manager =
ui::AXTreeManagerMap::GetInstance().GetManager(tree_id); ui::AXTreeManagerMap::GetInstance().GetManager(tree_id);
return manager ? manager->GetNodeFromTree(node_id) : nullptr; return manager ? manager->GetNodeFromTree(node_id) : nullptr;
...@@ -63,11 +70,14 @@ ui::AXNode* ViewsAXTreeManager::GetNodeFromTree( ...@@ -63,11 +70,14 @@ ui::AXNode* ViewsAXTreeManager::GetNodeFromTree(
ui::AXNode* ViewsAXTreeManager::GetNodeFromTree( ui::AXNode* ViewsAXTreeManager::GetNodeFromTree(
const ui::AXNode::AXID node_id) const { const ui::AXNode::AXID node_id) const {
if (!widget_)
return nullptr;
return ax_tree_.GetFromId(node_id); return ax_tree_.GetFromId(node_id);
} }
ui::AXTreeID ViewsAXTreeManager::GetTreeID() const { ui::AXTreeID ViewsAXTreeManager::GetTreeID() const {
return ax_tree_.GetAXTreeID(); return tree_id_;
} }
ui::AXTreeID ViewsAXTreeManager::GetParentTreeID() const { ui::AXTreeID ViewsAXTreeManager::GetParentTreeID() const {
...@@ -77,6 +87,9 @@ ui::AXTreeID ViewsAXTreeManager::GetParentTreeID() const { ...@@ -77,6 +87,9 @@ ui::AXTreeID ViewsAXTreeManager::GetParentTreeID() const {
} }
ui::AXNode* ViewsAXTreeManager::GetRootAsAXNode() const { ui::AXNode* ViewsAXTreeManager::GetRootAsAXNode() const {
if (!widget_)
return nullptr;
return ax_tree_.root(); return ax_tree_.root();
} }
...@@ -101,11 +114,31 @@ void ViewsAXTreeManager::OnViewEvent(View* view, ax::mojom::Event event) { ...@@ -101,11 +114,31 @@ void ViewsAXTreeManager::OnViewEvent(View* view, ax::mojom::Event event) {
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
void ViewsAXTreeManager::OnWidgetDestroyed(Widget* widget) {
if (widget->is_top_level())
views::WidgetAXTreeIDMap::GetInstance().RemoveWidget(widget);
widget_ = nullptr;
}
void ViewsAXTreeManager::OnWidgetClosing(Widget* widget) {
if (widget->is_top_level())
views::WidgetAXTreeIDMap::GetInstance().RemoveWidget(widget);
widget_ = nullptr;
}
void ViewsAXTreeManager::PerformAction(const ui::AXActionData& data) { void ViewsAXTreeManager::PerformAction(const ui::AXActionData& data) {
if (!widget_)
return;
tree_source_.HandleAccessibleAction(data); tree_source_.HandleAccessibleAction(data);
} }
void ViewsAXTreeManager::SerializeTreeUpdates() { void ViewsAXTreeManager::SerializeTreeUpdates() {
if (!widget_)
return;
// Better to set this flag to false early in case this method, or any method // Better to set this flag to false early in case this method, or any method
// it calls, causes an event to get fired. // it calls, causes an event to get fired.
waiting_to_serialize_ = false; waiting_to_serialize_ = false;
...@@ -139,6 +172,9 @@ void ViewsAXTreeManager::SerializeTreeUpdates() { ...@@ -139,6 +172,9 @@ void ViewsAXTreeManager::SerializeTreeUpdates() {
void ViewsAXTreeManager::UnserializeTreeUpdates( void ViewsAXTreeManager::UnserializeTreeUpdates(
const std::vector<ui::AXTreeUpdate>& updates) { const std::vector<ui::AXTreeUpdate>& updates) {
if (!widget_)
return;
for (const ui::AXTreeUpdate& update : updates) { for (const ui::AXTreeUpdate& update : updates) {
if (!ax_tree_.Unserialize(update)) { if (!ax_tree_.Unserialize(update)) {
NOTREACHED() << ax_tree_.error(); NOTREACHED() << ax_tree_.error();
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h" #include "base/scoped_observation.h"
#include "ui/accessibility/ax_action_handler.h" #include "ui/accessibility/ax_action_handler.h"
#include "ui/accessibility/ax_enums.mojom-forward.h" #include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/accessibility/ax_event_generator.h" #include "ui/accessibility/ax_event_generator.h"
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include "ui/views/accessibility/ax_event_observer.h" #include "ui/views/accessibility/ax_event_observer.h"
#include "ui/views/accessibility/ax_tree_source_views.h" #include "ui/views/accessibility/ax_tree_source_views.h"
#include "ui/views/views_export.h" #include "ui/views/views_export.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace ui { namespace ui {
...@@ -38,7 +40,6 @@ namespace views { ...@@ -38,7 +40,6 @@ namespace views {
class AXAuraObjWrapper; class AXAuraObjWrapper;
class View; class View;
class Widget;
// Manages an accessibility tree that mirrors the Views tree for a particular // Manages an accessibility tree that mirrors the Views tree for a particular
// widget. // widget.
...@@ -51,7 +52,8 @@ class Widget; ...@@ -51,7 +52,8 @@ class Widget;
// deserialized into an AXTree, both in the same process. // deserialized into an AXTree, both in the same process.
class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
public ui::AXActionHandler, public ui::AXActionHandler,
public AXEventObserver { public AXEventObserver,
public views::WidgetObserver {
public: public:
using GeneratedEventCallbackForTesting = base::RepeatingCallback< using GeneratedEventCallbackForTesting = base::RepeatingCallback<
void(Widget*, ui::AXEventGenerator::Event, ui::AXNode::AXID)>; void(Widget*, ui::AXEventGenerator::Event, ui::AXNode::AXID)>;
...@@ -93,6 +95,10 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, ...@@ -93,6 +95,10 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
// AXEventObserver implementation. // AXEventObserver implementation.
void OnViewEvent(views::View* view, ax::mojom::Event event) override; void OnViewEvent(views::View* view, ax::mojom::Event event) override;
// WidgetObserver implementation.
void OnWidgetDestroyed(Widget* widget) override;
void OnWidgetClosing(Widget* widget) override;
private: private:
using ViewsAXTreeSerializer = using ViewsAXTreeSerializer =
ui::AXTreeSerializer<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>; ui::AXTreeSerializer<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>;
...@@ -110,7 +116,7 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, ...@@ -110,7 +116,7 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
// The Widget for which this class manages an AXTree. // The Widget for which this class manages an AXTree.
// //
// Weak, a Widget doesn't own this class. // Weak, a Widget doesn't own this class.
Widget* const widget_; Widget* widget_;
// Set to true if we are still waiting for a task to serialize all previously // Set to true if we are still waiting for a task to serialize all previously
// modified nodes. // modified nodes.
...@@ -124,6 +130,13 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, ...@@ -124,6 +130,13 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
// that are used to serialize the Views tree. // that are used to serialize the Views tree.
AXAuraObjCache cache_; AXAuraObjCache cache_;
// The ID for this AXTree.
ui::AXTreeID tree_id_;
// The AXTree that mirrors the Views tree and which is created by
// deserializing the updates from |tree_source_|.
ui::AXTree ax_tree_;
// The tree source that enables us to serialize the Views tree. // The tree source that enables us to serialize the Views tree.
AXTreeSourceViews tree_source_; AXTreeSourceViews tree_source_;
...@@ -131,10 +144,6 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, ...@@ -131,10 +144,6 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
// AXTreeUpdate. // AXTreeUpdate.
ViewsAXTreeSerializer tree_serializer_; ViewsAXTreeSerializer tree_serializer_;
// The AXTree that mirrors the Views tree and which is created by
// deserializing the updates from |tree_source_|.
ui::AXTree ax_tree_;
// For automatically generating events based on changes to |tree_|. // For automatically generating events based on changes to |tree_|.
ui::AXEventGenerator event_generator_; ui::AXEventGenerator event_generator_;
...@@ -143,7 +152,9 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager, ...@@ -143,7 +152,9 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
// To prevent any use-after-free, members below this line should be declared // To prevent any use-after-free, members below this line should be declared
// last. // last.
ScopedObserver<AXEventManager, AXEventObserver> views_event_observer_{this}; base::ScopedObservation<AXEventManager, AXEventObserver>
views_event_observer_{this};
base::ScopedObservation<Widget, views::WidgetObserver> widget_observer_{this};
base::WeakPtrFactory<ViewsAXTreeManager> weak_factory_{this}; base::WeakPtrFactory<ViewsAXTreeManager> weak_factory_{this};
}; };
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event_generator.h" #include "ui/accessibility/ax_event_generator.h"
#include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_node.h"
...@@ -41,7 +43,8 @@ class TestButton : public Button { ...@@ -41,7 +43,8 @@ class TestButton : public Button {
~TestButton() override = default; ~TestButton() override = default;
}; };
class ViewsAXTreeManagerTest : public ViewsTestBase { class ViewsAXTreeManagerTest : public ViewsTestBase,
public ::testing::WithParamInterface<bool> {
public: public:
ViewsAXTreeManagerTest() = default; ViewsAXTreeManagerTest() = default;
~ViewsAXTreeManagerTest() override = default; ~ViewsAXTreeManagerTest() override = default;
...@@ -51,6 +54,7 @@ class ViewsAXTreeManagerTest : public ViewsTestBase { ...@@ -51,6 +54,7 @@ class ViewsAXTreeManagerTest : public ViewsTestBase {
protected: protected:
void SetUp() override; void SetUp() override;
void TearDown() override; void TearDown() override;
void CloseWidget();
ui::AXNode* FindNode(const ax::mojom::Role role, ui::AXNode* FindNode(const ax::mojom::Role role,
const std::string& name_or_value) const; const std::string& name_or_value) const;
void WaitFor(const ui::AXEventGenerator::Event event); void WaitFor(const ui::AXEventGenerator::Event event);
...@@ -58,7 +62,7 @@ class ViewsAXTreeManagerTest : public ViewsTestBase { ...@@ -58,7 +62,7 @@ class ViewsAXTreeManagerTest : public ViewsTestBase {
Widget* widget() const { return widget_; } Widget* widget() const { return widget_; }
Button* button() const { return button_; } Button* button() const { return button_; }
Label* label() const { return label_; } Label* label() const { return label_; }
const ViewsAXTreeManager& manager() const { return *manager_; } ViewsAXTreeManager* manager() const { return manager_.get(); }
private: private:
ui::AXNode* FindNodeInSubtree(ui::AXNode* root, ui::AXNode* FindNodeInSubtree(ui::AXNode* root,
...@@ -74,13 +78,20 @@ class ViewsAXTreeManagerTest : public ViewsTestBase { ...@@ -74,13 +78,20 @@ class ViewsAXTreeManagerTest : public ViewsTestBase {
std::unique_ptr<ViewsAXTreeManager> manager_; std::unique_ptr<ViewsAXTreeManager> manager_;
ui::AXEventGenerator::Event event_to_wait_for_; ui::AXEventGenerator::Event event_to_wait_for_;
std::unique_ptr<base::RunLoop> loop_runner_; std::unique_ptr<base::RunLoop> loop_runner_;
base::test::ScopedFeatureList scoped_feature_list_;
}; };
void ViewsAXTreeManagerTest::SetUp() { void ViewsAXTreeManagerTest::SetUp() {
ViewsTestBase::SetUp(); ViewsTestBase::SetUp();
if (GetParam()) {
scoped_feature_list_.InitWithFeatures(
{features::kEnableAccessibilityTreeForViews}, {});
}
widget_ = new Widget; widget_ = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(0, 0, 200, 200); params.bounds = gfx::Rect(0, 0, 200, 200);
widget_->Init(std::move(params)); widget_->Init(std::move(params));
...@@ -93,26 +104,45 @@ void ViewsAXTreeManagerTest::SetUp() { ...@@ -93,26 +104,45 @@ void ViewsAXTreeManagerTest::SetUp() {
widget_->GetContentsView()->AddChildView(button_); widget_->GetContentsView()->AddChildView(button_);
widget_->Show(); widget_->Show();
manager_ = std::make_unique<ViewsAXTreeManager>(widget_); // AccessibilityTreeForViewsEnabled will create and manage its own
ASSERT_NE(nullptr, manager_.get()); // ViewsAXTreeManager, so we don't need to create one for testing.
manager_->SetGeneratedEventCallbackForTesting(base::BindRepeating( if (features::IsAccessibilityTreeForViewsEnabled()) {
manager_.reset(
widget_->GetRootView()->GetViewAccessibility().AXTreeManager());
} else {
manager_ = std::make_unique<ViewsAXTreeManager>(widget_);
}
ASSERT_NE(nullptr, manager_);
manager()->SetGeneratedEventCallbackForTesting(base::BindRepeating(
&ViewsAXTreeManagerTest::OnGeneratedEvent, base::Unretained(this))); &ViewsAXTreeManagerTest::OnGeneratedEvent, base::Unretained(this)));
WaitFor(ui::AXEventGenerator::Event::LOAD_COMPLETE); WaitFor(ui::AXEventGenerator::Event::LOAD_COMPLETE);
} }
void ViewsAXTreeManagerTest::TearDown() { void ViewsAXTreeManagerTest::TearDown() {
manager_->UnsetGeneratedEventCallbackForTesting(); if (manager())
manager()->UnsetGeneratedEventCallbackForTesting();
manager_.reset(); manager_.reset();
if (!widget_->IsClosed()) CloseWidget();
widget_->Close();
ViewsTestBase::TearDown(); ViewsTestBase::TearDown();
} }
void ViewsAXTreeManagerTest::CloseWidget() {
if (!widget_->IsClosed())
widget_->CloseNow();
RunPendingMessages();
}
ui::AXNode* ViewsAXTreeManagerTest::FindNode( ui::AXNode* ViewsAXTreeManagerTest::FindNode(
const ax::mojom::Role role, const ax::mojom::Role role,
const std::string& name_or_value) const { const std::string& name_or_value) const {
ui::AXNode* root = manager_->GetRootAsAXNode(); ui::AXNode* root = manager()->GetRootAsAXNode();
EXPECT_NE(nullptr, root);
// If the manager has been closed, it will return nullptr as root.
if (!root)
return nullptr;
return FindNodeInSubtree(root, role, name_or_value); return FindNodeInSubtree(root, role, name_or_value);
} }
...@@ -149,15 +179,16 @@ ui::AXNode* ViewsAXTreeManagerTest::FindNodeInSubtree( ...@@ -149,15 +179,16 @@ ui::AXNode* ViewsAXTreeManagerTest::FindNodeInSubtree(
void ViewsAXTreeManagerTest::OnGeneratedEvent(Widget* widget, void ViewsAXTreeManagerTest::OnGeneratedEvent(Widget* widget,
ui::AXEventGenerator::Event event, ui::AXEventGenerator::Event event,
ui::AXNode::AXID node_id) { ui::AXNode::AXID node_id) {
ASSERT_NE(nullptr, manager_.get()) ASSERT_NE(nullptr, manager()) << "Should not be called after TearDown().";
<< "Should not be called after TearDown().";
if (loop_runner_ && event == event_to_wait_for_) if (loop_runner_ && event == event_to_wait_for_)
loop_runner_->Quit(); loop_runner_->Quit();
} }
} // namespace } // namespace
TEST_F(ViewsAXTreeManagerTest, MirrorInitialTree) { INSTANTIATE_TEST_SUITE_P(All, ViewsAXTreeManagerTest, testing::Bool());
TEST_P(ViewsAXTreeManagerTest, MirrorInitialTree) {
ui::AXNodeData button_data; ui::AXNodeData button_data;
button()->GetViewAccessibility().GetAccessibleNodeData(&button_data); button()->GetViewAccessibility().GetAccessibleNodeData(&button_data);
ui::AXNode* ax_button = FindNode(ax::mojom::Role::kButton, ""); ui::AXNode* ax_button = FindNode(ax::mojom::Role::kButton, "");
...@@ -174,7 +205,7 @@ TEST_F(ViewsAXTreeManagerTest, MirrorInitialTree) { ...@@ -174,7 +205,7 @@ TEST_F(ViewsAXTreeManagerTest, MirrorInitialTree) {
EXPECT_TRUE(ax_button->data().HasState(ax::mojom::State::kFocusable)); EXPECT_TRUE(ax_button->data().HasState(ax::mojom::State::kFocusable));
} }
TEST_F(ViewsAXTreeManagerTest, PerformAction) { TEST_P(ViewsAXTreeManagerTest, PerformAction) {
ui::AXNode* ax_button = FindNode(ax::mojom::Role::kButton, ""); ui::AXNode* ax_button = FindNode(ax::mojom::Role::kButton, "");
ASSERT_NE(nullptr, ax_button); ASSERT_NE(nullptr, ax_button);
ASSERT_FALSE(ax_button->data().HasIntAttribute( ASSERT_FALSE(ax_button->data().HasIntAttribute(
...@@ -185,5 +216,22 @@ TEST_F(ViewsAXTreeManagerTest, PerformAction) { ...@@ -185,5 +216,22 @@ TEST_F(ViewsAXTreeManagerTest, PerformAction) {
WaitFor(ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED); WaitFor(ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED);
} }
TEST_P(ViewsAXTreeManagerTest, CloseWidget) {
// This test is only relevant when IsAccessibilityTreeForViewsEnabled is set,
// as it tests the lifetime management of ViewsAXTreeManager when a Widget is
// closed.
if (!features::IsAccessibilityTreeForViewsEnabled())
return;
ui::AXNode* ax_button = FindNode(ax::mojom::Role::kButton, "");
ASSERT_NE(nullptr, ax_button);
CloseWidget();
// Looking up a node after its Widget has been closed should return nullptr.
ax_button = FindNode(ax::mojom::Role::kButton, "");
EXPECT_EQ(nullptr, ax_button);
}
} // namespace test } // namespace test
} // namespace views } // namespace views
// Copyright 2020 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.
#include "ui/views/accessibility/widget_ax_tree_id_map.h"
#include "base/stl_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
namespace views {
WidgetAXTreeIDMap::WidgetAXTreeIDMap() {}
WidgetAXTreeIDMap::~WidgetAXTreeIDMap() {}
// static
WidgetAXTreeIDMap& WidgetAXTreeIDMap::GetInstance() {
static base::NoDestructor<WidgetAXTreeIDMap> instance;
return *instance;
}
bool WidgetAXTreeIDMap::HasWidget(Widget* widget) {
return base::Contains(widget_map_, widget);
}
void WidgetAXTreeIDMap::AddWidget(ui::AXTreeID tree_id, Widget* widget) {
DCHECK_NE(tree_id, ui::AXTreeIDUnknown());
DCHECK(widget);
DCHECK(!HasWidget(widget));
widget_map_[widget] = tree_id;
}
void WidgetAXTreeIDMap::RemoveWidget(Widget* widget) {
widget_map_.erase(widget);
}
ui::AXTreeID WidgetAXTreeIDMap::GetWidgetTreeID(Widget* widget) {
DCHECK(widget);
if (!base::Contains(widget_map_, widget))
return ui::AXTreeIDUnknown();
return widget_map_.at(widget);
}
const std::vector<Widget*> WidgetAXTreeIDMap::GetWidgets() const {
std::vector<Widget*> widgets;
widgets.reserve(widget_map_.size());
for (auto iter : widget_map_)
widgets.push_back(iter.first);
return widgets;
}
} // namespace views
// Copyright 2020 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_VIEWS_ACCESSIBILITY_WIDGET_AX_TREE_ID_MAP_H_
#define UI_VIEWS_ACCESSIBILITY_WIDGET_AX_TREE_ID_MAP_H_
#include <map>
#include <vector>
#include "base/macros.h"
#include "base/no_destructor.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/accessibility/ax_tree_manager.h"
#include "ui/views/views_export.h"
namespace views {
class Widget;
// This class manages mapping between Widgets and their associated AXTreeIDs.
// It is a singleton wrapper around a std::map. Widget pointers are used as the
// key for the map and AXTreeID's are used as the value returned.
class VIEWS_EXPORT WidgetAXTreeIDMap {
public:
WidgetAXTreeIDMap();
~WidgetAXTreeIDMap();
static WidgetAXTreeIDMap& GetInstance();
bool HasWidget(Widget* widget);
void AddWidget(ui::AXTreeID tree_id, Widget* widget);
void RemoveWidget(Widget* widget);
ui::AXTreeID GetWidgetTreeID(views::Widget* widget);
const std::vector<Widget*> GetWidgets() const;
private:
std::map<Widget*, ui::AXTreeID> widget_map_;
};
} // namespace views
#endif // UI_VIEWS_ACCESSIBILITY_WIDGET_AX_TREE_ID_MAP_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment