#include "ui/accessibility/platform/ax_platform_relation_win.h"
#include <wrl/client.h>
#include <vector>
#include "base/compiler_specific.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
#include "ui/base/win/atl_module.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace ui {
AXPlatformRelationWin::AXPlatformRelationWin() {
win::CreateATLModuleIfNeeded();
}
AXPlatformRelationWin::~AXPlatformRelationWin() {}
std::wstring GetIA2RelationFromIntAttr(ax::mojom::IntAttribute attribute) {
switch (attribute) {
case ax::mojom::IntAttribute::kMemberOfId:
return IA2_RELATION_MEMBER_OF;
case ax::mojom::IntAttribute::kPopupForId:
return IA2_RELATION_CONTROLLED_BY;
default:
return std::wstring();
}
}
std::wstring GetIA2RelationFromIntListAttr(
ax::mojom::IntListAttribute attribute) {
switch (attribute) {
case ax::mojom::IntListAttribute::kControlsIds:
return IA2_RELATION_CONTROLLER_FOR;
case ax::mojom::IntListAttribute::kDescribedbyIds:
return IA2_RELATION_DESCRIBED_BY;
case ax::mojom::IntListAttribute::kDetailsIds:
return IA2_RELATION_DETAILS;
case ax::mojom::IntListAttribute::kErrormessageIds:
return IA2_RELATION_ERROR;
case ax::mojom::IntListAttribute::kFlowtoIds:
return IA2_RELATION_FLOWS_TO;
case ax::mojom::IntListAttribute::kLabelledbyIds:
return IA2_RELATION_LABELLED_BY;
default:
return std::wstring();
}
}
std::wstring GetIA2ReverseRelationFromIntAttr(
ax::mojom::IntAttribute attribute) {
switch (attribute) {
default:
return std::wstring();
}
}
std::wstring GetIA2ReverseRelationFromIntListAttr(
ax::mojom::IntListAttribute attribute) {
switch (attribute) {
case ax::mojom::IntListAttribute::kControlsIds:
return IA2_RELATION_CONTROLLED_BY;
case ax::mojom::IntListAttribute::kDescribedbyIds:
return IA2_RELATION_DESCRIPTION_FOR;
case ax::mojom::IntListAttribute::kDetailsIds:
return IA2_RELATION_DETAILS_FOR;
case ax::mojom::IntListAttribute::kErrormessageIds:
return IA2_RELATION_ERROR_FOR;
case ax::mojom::IntListAttribute::kFlowtoIds:
return IA2_RELATION_FLOWS_FROM;
case ax::mojom::IntListAttribute::kLabelledbyIds:
return IA2_RELATION_LABEL_FOR;
default:
return std::wstring();
}
}
int AXPlatformRelationWin::EnumerateRelationships(
AXPlatformNodeBase* node,
int desired_index,
const std::wstring& desired_ia2_relation,
std::wstring* out_ia2_relation,
std::vector<AXPlatformNode*>* out_targets) {
AXPlatformNodeDelegate* delegate = node->GetDelegate();
static base::NoDestructor<std::vector<ax::mojom::IntAttribute>>
int_attributes_with_reverse_relations;
static base::NoDestructor<std::vector<ax::mojom::IntListAttribute>>
intlist_attributes_with_reverse_relations;
static bool first_time = true;
if (first_time) {
for (int32_t attr_index =
static_cast<int32_t>(ax::mojom::IntAttribute::kNone);
attr_index <= static_cast<int32_t>(ax::mojom::IntAttribute::kMaxValue);
++attr_index) {
auto attr = static_cast<ax::mojom::IntAttribute>(attr_index);
if (!GetIA2ReverseRelationFromIntAttr(attr).empty())
int_attributes_with_reverse_relations->push_back(attr);
}
for (int32_t attr_index =
static_cast<int32_t>(ax::mojom::IntListAttribute::kNone);
attr_index <=
static_cast<int32_t>(ax::mojom::IntListAttribute::kMaxValue);
++attr_index) {
auto attr = static_cast<ax::mojom::IntListAttribute>(attr_index);
if (!GetIA2ReverseRelationFromIntListAttr(attr).empty())
intlist_attributes_with_reverse_relations->push_back(attr);
}
first_time = false;
}
int total_count = 0;
for (const auto& attribute_value_pair : node->GetIntAttributes()) {
ax::mojom::IntAttribute int_attribute = attribute_value_pair.first;
std::wstring relation = GetIA2RelationFromIntAttr(int_attribute);
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
AXPlatformNode* target =
delegate->GetTargetNodeForRelation(int_attribute);
if (!target) {
continue;
}
if (desired_index == total_count) {
*out_ia2_relation = relation;
out_targets->push_back(target);
return 1;
}
total_count++;
}
}
for (ax::mojom::IntAttribute int_attribute :
*int_attributes_with_reverse_relations) {
std::wstring relation = GetIA2ReverseRelationFromIntAttr(int_attribute);
std::vector<AXPlatformNode*> targets =
delegate->GetSourceNodesForReverseRelations(int_attribute);
if (targets.size()) {
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
*out_targets = targets;
return 1;
}
total_count++;
}
}
}
for (const auto& attribute_value_pair : node->GetIntListAttributes()) {
ax::mojom::IntListAttribute intlist_attribute = attribute_value_pair.first;
std::wstring relation = GetIA2RelationFromIntListAttr(intlist_attribute);
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
*out_targets = delegate->GetTargetNodesForRelation(intlist_attribute);
if (out_targets->size() == 0)
continue;
return 1;
}
total_count++;
}
}
for (ax::mojom::IntListAttribute intlist_attribute :
*intlist_attributes_with_reverse_relations) {
std::wstring relation =
GetIA2ReverseRelationFromIntListAttr(intlist_attribute);
std::vector<AXPlatformNode*> targets =
delegate->GetSourceNodesForReverseRelations(intlist_attribute);
if (targets.size()) {
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
*out_targets = targets;
return 1;
}
total_count++;
}
}
}
return total_count;
}
void AXPlatformRelationWin::Initialize(const std::wstring& type) {
type_ = type;
}
void AXPlatformRelationWin::Invalidate() {
targets_.clear();
}
void AXPlatformRelationWin::AddTarget(AXPlatformNodeWin* target) {
targets_.push_back(target);
}
IFACEMETHODIMP AXPlatformRelationWin::get_relationType(BSTR* relation_type) {
if (!relation_type)
return E_INVALIDARG;
*relation_type = SysAllocString(type_.c_str());
DCHECK(*relation_type);
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_nTargets(LONG* n_targets) {
if (!n_targets)
return E_INVALIDARG;
*n_targets = static_cast<LONG>(targets_.size());
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_target(LONG target_index,
IUnknown** target) {
if (!target)
return E_INVALIDARG;
if (target_index < 0 || target_index >= static_cast<LONG>(targets_.size())) {
return E_INVALIDARG;
}
*target = static_cast<IAccessible*>(targets_[target_index].Get());
(*target)->AddRef();
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_targets(LONG max_targets,
IUnknown** targets,
LONG* n_targets) {
if (!targets || !n_targets)
return E_INVALIDARG;
LONG count = static_cast<LONG>(targets_.size());
if (count > max_targets)
count = max_targets;
*n_targets = count;
if (count == 0)
return S_FALSE;
for (LONG i = 0; i < count; ++i) {
HRESULT result = get_target(i, &UNSAFE_TODO(targets[i]));
if (result != S_OK)
return result;
}
return S_OK;
}
IFACEMETHODIMP
AXPlatformRelationWin::get_localizedRelationType(BSTR* relation_type) {
return E_NOTIMPL;
}
}