#include "ui/views/accessibility/ax_window_obj_wrapper.h"
#include <stddef.h>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/aura/aura_window_properties.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window_tree_host.h"
#include "ui/aura/window_tree_host_platform.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/compositor/layer.h"
#include "ui/views/accessibility/ax_aura_obj_cache.h"
#include "ui/views/widget/widget.h"
namespace views {
namespace {
Widget* GetWidgetForWindow(aura::Window* window) {
Widget* widget = Widget::GetWidgetForNativeView(window);
if (!widget) {
return nullptr;
}
if (widget->GetNativeWindow() != window) {
DCHECK(window->IsRootWindow());
return nullptr;
}
return widget;
}
void FireEventOnWindowChildWidgetAndRootView(aura::Window* window,
ax::mojom::Event event,
AXAuraObjCache* cache) {
cache->FireEvent(cache->GetOrCreate(window), event);
Widget* widget = GetWidgetForWindow(window);
if (widget) {
cache->FireEvent(cache->GetOrCreate(widget), event);
views::View* root_view = widget->GetRootView();
if (root_view) {
root_view->NotifyAccessibilityEventDeprecated(event, true);
}
}
}
void FireLocationChangesRecursively(aura::Window* window,
AXAuraObjCache* cache) {
FireEventOnWindowChildWidgetAndRootView(
window, ax::mojom::Event::kLocationChanged, cache);
for (aura::Window* child : window->children()) {
FireLocationChangesRecursively(child, cache);
}
}
std::string GetWindowName(aura::Window* window) {
std::string class_name = window->GetName();
if (class_name.empty()) {
class_name = "aura::Window";
}
return class_name;
}
}
AXWindowObjWrapper::AXWindowObjWrapper(AXAuraObjCache* aura_obj_cache,
aura::Window* window)
: AXAuraObjWrapper(aura_obj_cache),
window_(window),
is_root_window_(window->IsRootWindow()) {
observation_.Observe(window);
if (is_root_window_) {
aura_obj_cache_->OnRootWindowObjCreated(window);
}
if (window->IsRootWindow() && !window->parent()) {
aura::WindowTreeHost* window_tree_host = window->GetHost();
if (window_tree_host) {
ime_observation_.Observe(window_tree_host->GetInputMethod());
}
}
}
AXWindowObjWrapper::~AXWindowObjWrapper() = default;
bool AXWindowObjWrapper::HandleAccessibleAction(
const ui::AXActionData& action) {
if (action.action == ax::mojom::Action::kFocus) {
window_->Focus();
return true;
}
return false;
}
AXAuraObjWrapper* AXWindowObjWrapper::GetParent() {
aura::Window* parent = window_->parent();
if (!parent) {
return nullptr;
}
if (parent->GetProperty(ui::kChildAXTreeID) &&
ui::AXTreeID::FromString(*(parent->GetProperty(ui::kChildAXTreeID))) !=
ui::AXTreeIDUnknown()) {
return nullptr;
}
return aura_obj_cache_->GetOrCreate(parent);
}
void AXWindowObjWrapper::GetChildren(
std::vector<raw_ptr<AXAuraObjWrapper, VectorExperimental>>* out_children) {
if (window_->GetProperty(ui::kChildAXTreeID) &&
ui::AXTreeID::FromString(*(window_->GetProperty(ui::kChildAXTreeID))) !=
ui::AXTreeIDUnknown()) {
return;
}
if (window_->GetProperty(ui::kAXConsiderInvisibleAndIgnoreChildren)) {
return;
}
for (aura::Window* child : window_->children()) {
out_children->push_back(aura_obj_cache_->GetOrCreate(child));
}
Widget* widget = GetWidgetForWindow(window_);
if (widget && widget->IsVisible()) {
out_children->push_back(aura_obj_cache_->GetOrCreate(widget));
}
}
void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) {
if (window_->IsRootWindow() && !window_->parent() && window_->GetHost()) {
ui::TextInputClient* client =
window_->GetHost()->GetInputMethod()->GetTextInputClient();
if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) {
gfx::Rect caret_bounds_in_screen = client->GetCaretBounds();
out_node_data->AddIntListAttribute(
ax::mojom::IntListAttribute::kCaretBounds,
{caret_bounds_in_screen.x(), caret_bounds_in_screen.y(),
caret_bounds_in_screen.width(), caret_bounds_in_screen.height()});
}
}
out_node_data->id = GetUniqueId();
ax::mojom::Role role = window_->GetProperty(ui::kAXRoleOverride);
if (role != ax::mojom::Role::kNone) {
out_node_data->role = role;
} else {
out_node_data->role = ax::mojom::Role::kWindow;
}
out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kName,
base::UTF16ToUTF8(window_->GetTitle()));
if (!window_->IsVisible() ||
window_->GetProperty(ui::kAXConsiderInvisibleAndIgnoreChildren)) {
out_node_data->AddState(ax::mojom::State::kInvisible);
}
out_node_data->relative_bounds.bounds =
gfx::RectF(window_->GetBoundsInScreen());
out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kClassName,
GetWindowName(window_));
std::string* child_ax_tree_id_ptr = window_->GetProperty(ui::kChildAXTreeID);
if (child_ax_tree_id_ptr) {
ui::AXTreeID child_ax_tree_id =
ui::AXTreeID::FromString(*child_ax_tree_id_ptr);
if (child_ax_tree_id != ui::AXTreeIDUnknown()) {
if (!window_->GetToplevelWindow() ||
GetWidgetForWindow(window_->GetToplevelWindow()) ||
!window_->IsVisible() ||
window_->GetProperty(ui::kAXConsiderInvisibleAndIgnoreChildren)) {
return;
}
out_node_data->AddChildTreeId(child_ax_tree_id);
const float scale_factor =
window_->GetToplevelWindow()->layer()->device_scale_factor();
out_node_data->AddFloatAttribute(
ax::mojom::FloatAttribute::kChildTreeScale, scale_factor);
}
}
}
ui::AXNodeID AXWindowObjWrapper::GetUniqueId() const {
return unique_id_.Get();
}
std::string AXWindowObjWrapper::ToString() const {
return GetWindowName(window_);
}
void AXWindowObjWrapper::OnCaretBoundsChanged(
const ui::TextInputClient* client) {
FireEvent(ax::mojom::Event::kTreeChanged);
}
void AXWindowObjWrapper::OnWindowDestroyed(aura::Window* window) {
if (is_root_window_) {
aura_obj_cache_->OnRootWindowObjDestroyed(window_);
}
aura_obj_cache_->Remove(window, nullptr);
}
void AXWindowObjWrapper::OnWindowDestroying(aura::Window* window) {
DCHECK_EQ(window, window_);
window_destroying_ = true;
}
void AXWindowObjWrapper::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
if (params.phase ==
WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED) {
aura_obj_cache_->Remove(params.target, params.old_parent);
}
}
void AXWindowObjWrapper::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
if (window_destroying_) {
return;
}
if (window == window_) {
FireLocationChangesRecursively(window_, aura_obj_cache_);
}
}
void AXWindowObjWrapper::OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) {
if (window_destroying_ || window != window_) {
return;
}
if (key == ui::kChildAXTreeID ||
key == ui::kAXConsiderInvisibleAndIgnoreChildren) {
FireEvent(ax::mojom::Event::kChildrenChanged);
}
}
void AXWindowObjWrapper::OnWindowVisibilityChanged(aura::Window* window,
bool visible) {
if (window_destroying_) {
return;
}
FireEvent(ax::mojom::Event::kStateChanged);
}
void AXWindowObjWrapper::OnWindowTransformed(aura::Window* window,
ui::PropertyChangeReason reason) {
if (window_destroying_) {
return;
}
if (window == window_) {
FireLocationChangesRecursively(window_, aura_obj_cache_);
}
}
void AXWindowObjWrapper::OnWindowTitleChanged(aura::Window* window) {
if (window_destroying_) {
return;
}
FireEventOnWindowChildWidgetAndRootView(
window_, ax::mojom::Event::kTreeChanged, aura_obj_cache_);
}
void AXWindowObjWrapper::FireEvent(ax::mojom::Event event_type) {
aura_obj_cache_->FireEvent(aura_obj_cache_->GetOrCreate(window_), event_type);
}
}