1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/accessibility/view_accessibility_utils.h"
#include <algorithm>
#include <set>
#include <string>
#include "base/memory/raw_ptr.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace views {
// static
Widget* ViewAccessibilityUtils::GetFocusedChildWidgetForAccessibility(
const View* view) {
const FocusManager* focus_manager = view->GetFocusManager();
if (!focus_manager) {
return nullptr;
}
const View* focused_view = view->GetFocusManager()->GetFocusedView();
if (!focused_view) {
return nullptr;
}
Widget::Widgets child_widgets =
Widget::GetAllOwnedWidgets(view->GetWidget()->GetNativeView());
const auto i =
std::ranges::find_if(child_widgets, [focused_view](Widget* child_widget) {
return IsFocusedChildWidget(child_widget, focused_view);
});
return (i == child_widgets.cend()) ? nullptr : *i;
}
// static
bool ViewAccessibilityUtils::IsFocusedChildWidget(Widget* widget,
const View* focused_view) {
return widget->IsVisible() &&
widget->GetContentsView()->Contains(focused_view);
}
// static
void ViewAccessibilityUtils::Merge(const ui::AXNodeData& source,
ui::AXNodeData& destination) {
if (source.role != ax::mojom::Role::kUnknown) {
destination.role = source.role;
}
for (const auto& attr : source.int_attributes) {
destination.AddIntAttribute(attr.first, attr.second);
}
for (const auto& attr : source.string_attributes) {
// Child Tree ID attribute must be added using AddChildTreeId, otherwise
// DCHECK will be hit.
if (attr.first == ax::mojom::StringAttribute::kChildTreeId) {
destination.AddChildTreeId(ui::AXTreeID::FromString(attr.second));
} else {
destination.AddStringAttribute(attr.first, attr.second);
}
}
for (const auto& attr : source.bool_attributes) {
destination.AddBoolAttribute(attr.first, attr.second);
}
for (const auto& attr : source.intlist_attributes) {
destination.AddIntListAttribute(attr.first, attr.second);
}
for (const auto& attr : source.stringlist_attributes) {
destination.AddStringListAttribute(attr.first, attr.second);
}
for (const auto& attr : source.float_attributes) {
destination.AddFloatAttribute(attr.first, attr.second);
}
if (!source.relative_bounds.bounds.IsEmpty()) {
destination.relative_bounds.bounds = source.relative_bounds.bounds;
}
if (source.id != ui::kInvalidAXNodeID) {
destination.id = source.id;
}
destination.state |= source.state;
destination.actions |= source.actions;
}
// static
void ViewAccessibilityUtils::ValidateAttributesNotSet(
const ui::AXNodeData& new_data,
const ui::AXNodeData& existing_data) {
auto attributeErrorMessage = [](std::string attr) -> std::string {
return "The \"" + attr +
"\" attribute has been migrated to use the new AXNodeData pipeline. "
"Please use "
"the setters/getters in ViewAccessibility to set the attribute."
"See the comment in ViewAccessibility::GetAccessibleNodeData for "
"more info.";
};
for (const auto& attr : new_data.int_attributes) {
DCHECK(!existing_data.HasIntAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
for (const auto& attr : new_data.string_attributes) {
DCHECK(!existing_data.HasStringAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
for (const auto& attr : new_data.bool_attributes) {
DCHECK(!existing_data.HasBoolAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
for (const auto& attr : new_data.float_attributes) {
DCHECK(!existing_data.HasFloatAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
for (const auto& attr : new_data.intlist_attributes) {
DCHECK(!existing_data.HasIntListAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
for (const auto& attr : new_data.stringlist_attributes) {
DCHECK(!existing_data.HasStringListAttribute(attr.first))
<< attributeErrorMessage(std::string(ui::ToString(attr.first)));
}
auto bitfieldErrorMessage = [](std::string bitfield_name) -> std::string {
return "The accessible " + bitfield_name +
" should be set directly in the accessibility cache through the "
"ViewAccessibility setters, not through this lazy loading "
"mechanism. The purpose of the lazy loading mechanism is to avoid "
"expensive memory allocations and calculations until the "
"accessibility tree is actually needed. However, the state is "
"stored efficiently in a bitfield always initialized to zero, so "
"there's not performance improvement to be gained by lazy loading "
"it.";
};
DCHECK(new_data.state == 0U) << bitfieldErrorMessage("state");
DCHECK(new_data.actions == 0U) << bitfieldErrorMessage("action");
DCHECK(new_data.relative_bounds.bounds.IsEmpty())
<< "The `relative_bounds` should not be set in the lazy loading "
"function. Instead, use `ViewAccessibility::SetBounds.`";
}
} // namespace views
|