mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
1733 lines
63 KiB
C++
1733 lines
63 KiB
C++
// Copyright 2013 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/accessibility/ax_node_data.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
#include <utility>
|
|
|
|
#include "base/no_destructor.h"
|
|
#include "base/stl_util.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "ui/accessibility/ax_enum_util.h"
|
|
#include "ui/accessibility/ax_enums.mojom.h"
|
|
#include "ui/accessibility/ax_role_properties.h"
|
|
#include "ui/gfx/transform.h"
|
|
|
|
namespace ui {
|
|
|
|
namespace {
|
|
|
|
bool IsFlagSet(uint32_t bitfield, uint32_t flag) {
|
|
return (bitfield & (1U << flag)) != 0;
|
|
}
|
|
|
|
bool IsFlagSet(uint64_t bitfield, uint32_t flag) {
|
|
return (bitfield & (1ULL << flag)) != 0;
|
|
}
|
|
|
|
uint32_t ModifyFlag(uint32_t bitfield, uint32_t flag, bool set) {
|
|
return set ? (bitfield |= (1U << flag)) : (bitfield &= ~(1U << flag));
|
|
}
|
|
|
|
uint64_t ModifyFlag(uint64_t bitfield, uint32_t flag, bool set) {
|
|
return set ? (bitfield |= (1ULL << flag)) : (bitfield &= ~(1ULL << flag));
|
|
}
|
|
|
|
std::string StateBitfieldToString(uint32_t state_enum) {
|
|
std::string str;
|
|
for (uint32_t i = static_cast<uint32_t>(ax::mojom::State::kNone) + 1;
|
|
i <= static_cast<uint32_t>(ax::mojom::State::kMaxValue); ++i) {
|
|
if (IsFlagSet(state_enum, i))
|
|
str += " " +
|
|
base::ToUpperASCII(ui::ToString(static_cast<ax::mojom::State>(i)));
|
|
}
|
|
return str;
|
|
}
|
|
|
|
std::string ActionsBitfieldToString(uint64_t actions) {
|
|
std::string str;
|
|
for (uint32_t i = static_cast<uint32_t>(ax::mojom::Action::kNone) + 1;
|
|
i <= static_cast<uint32_t>(ax::mojom::Action::kMaxValue); ++i) {
|
|
if (IsFlagSet(actions, i)) {
|
|
str += ui::ToString(static_cast<ax::mojom::Action>(i));
|
|
actions = ModifyFlag(actions, i, false);
|
|
str += actions ? "," : "";
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
std::string IntVectorToString(const std::vector<int>& items) {
|
|
std::string str;
|
|
for (size_t i = 0; i < items.size(); ++i) {
|
|
if (i > 0)
|
|
str += ",";
|
|
str += base::NumberToString(items[i]);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
// Predicate that returns true if the first value of a pair is |first|.
|
|
template <typename FirstType, typename SecondType>
|
|
struct FirstIs {
|
|
explicit FirstIs(FirstType first) : first_(first) {}
|
|
bool operator()(std::pair<FirstType, SecondType> const& p) {
|
|
return p.first == first_;
|
|
}
|
|
FirstType first_;
|
|
};
|
|
|
|
// Helper function that finds a key in a vector of pairs by matching on the
|
|
// first value, and returns an iterator.
|
|
template <typename FirstType, typename SecondType>
|
|
typename std::vector<std::pair<FirstType, SecondType>>::const_iterator
|
|
FindInVectorOfPairs(
|
|
FirstType first,
|
|
const std::vector<std::pair<FirstType, SecondType>>& vector) {
|
|
return std::find_if(vector.begin(), vector.end(),
|
|
FirstIs<FirstType, SecondType>(first));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Return true if |attr| is a node ID that would need to be mapped when
|
|
// renumbering the ids in a combined tree.
|
|
bool IsNodeIdIntAttribute(ax::mojom::IntAttribute attr) {
|
|
switch (attr) {
|
|
case ax::mojom::IntAttribute::kActivedescendantId:
|
|
case ax::mojom::IntAttribute::kErrormessageId:
|
|
case ax::mojom::IntAttribute::kInPageLinkTargetId:
|
|
case ax::mojom::IntAttribute::kMemberOfId:
|
|
case ax::mojom::IntAttribute::kNextOnLineId:
|
|
case ax::mojom::IntAttribute::kPopupForId:
|
|
case ax::mojom::IntAttribute::kPreviousOnLineId:
|
|
case ax::mojom::IntAttribute::kTableHeaderId:
|
|
case ax::mojom::IntAttribute::kTableColumnHeaderId:
|
|
case ax::mojom::IntAttribute::kTableRowHeaderId:
|
|
case ax::mojom::IntAttribute::kNextFocusId:
|
|
case ax::mojom::IntAttribute::kPreviousFocusId:
|
|
return true;
|
|
|
|
// Note: all of the attributes are included here explicitly,
|
|
// rather than using "default:", so that it's a compiler error to
|
|
// add a new attribute without explicitly considering whether it's
|
|
// a node id attribute or not.
|
|
case ax::mojom::IntAttribute::kNone:
|
|
case ax::mojom::IntAttribute::kDefaultActionVerb:
|
|
case ax::mojom::IntAttribute::kScrollX:
|
|
case ax::mojom::IntAttribute::kScrollXMin:
|
|
case ax::mojom::IntAttribute::kScrollXMax:
|
|
case ax::mojom::IntAttribute::kScrollY:
|
|
case ax::mojom::IntAttribute::kScrollYMin:
|
|
case ax::mojom::IntAttribute::kScrollYMax:
|
|
case ax::mojom::IntAttribute::kTextSelStart:
|
|
case ax::mojom::IntAttribute::kTextSelEnd:
|
|
case ax::mojom::IntAttribute::kTableRowCount:
|
|
case ax::mojom::IntAttribute::kTableColumnCount:
|
|
case ax::mojom::IntAttribute::kTableRowIndex:
|
|
case ax::mojom::IntAttribute::kTableColumnIndex:
|
|
case ax::mojom::IntAttribute::kTableCellColumnIndex:
|
|
case ax::mojom::IntAttribute::kTableCellColumnSpan:
|
|
case ax::mojom::IntAttribute::kTableCellRowIndex:
|
|
case ax::mojom::IntAttribute::kTableCellRowSpan:
|
|
case ax::mojom::IntAttribute::kSortDirection:
|
|
case ax::mojom::IntAttribute::kHierarchicalLevel:
|
|
case ax::mojom::IntAttribute::kNameFrom:
|
|
case ax::mojom::IntAttribute::kDescriptionFrom:
|
|
case ax::mojom::IntAttribute::kSetSize:
|
|
case ax::mojom::IntAttribute::kPosInSet:
|
|
case ax::mojom::IntAttribute::kColorValue:
|
|
case ax::mojom::IntAttribute::kAriaCurrentState:
|
|
case ax::mojom::IntAttribute::kHasPopup:
|
|
case ax::mojom::IntAttribute::kBackgroundColor:
|
|
case ax::mojom::IntAttribute::kColor:
|
|
case ax::mojom::IntAttribute::kInvalidState:
|
|
case ax::mojom::IntAttribute::kCheckedState:
|
|
case ax::mojom::IntAttribute::kRestriction:
|
|
case ax::mojom::IntAttribute::kListStyle:
|
|
case ax::mojom::IntAttribute::kTextAlign:
|
|
case ax::mojom::IntAttribute::kTextDirection:
|
|
case ax::mojom::IntAttribute::kTextPosition:
|
|
case ax::mojom::IntAttribute::kTextStyle:
|
|
case ax::mojom::IntAttribute::kTextOverlineStyle:
|
|
case ax::mojom::IntAttribute::kTextStrikethroughStyle:
|
|
case ax::mojom::IntAttribute::kTextUnderlineStyle:
|
|
case ax::mojom::IntAttribute::kAriaColumnCount:
|
|
case ax::mojom::IntAttribute::kAriaCellColumnIndex:
|
|
case ax::mojom::IntAttribute::kAriaCellColumnSpan:
|
|
case ax::mojom::IntAttribute::kAriaRowCount:
|
|
case ax::mojom::IntAttribute::kAriaCellRowIndex:
|
|
case ax::mojom::IntAttribute::kAriaCellRowSpan:
|
|
case ax::mojom::IntAttribute::kImageAnnotationStatus:
|
|
case ax::mojom::IntAttribute::kDropeffect:
|
|
case ax::mojom::IntAttribute::kDOMNodeId:
|
|
return false;
|
|
}
|
|
|
|
NOTREACHED();
|
|
return false;
|
|
}
|
|
|
|
// Return true if |attr| contains a vector of node ids that would need
|
|
// to be mapped when renumbering the ids in a combined tree.
|
|
bool IsNodeIdIntListAttribute(ax::mojom::IntListAttribute attr) {
|
|
switch (attr) {
|
|
case ax::mojom::IntListAttribute::kControlsIds:
|
|
case ax::mojom::IntListAttribute::kDetailsIds:
|
|
case ax::mojom::IntListAttribute::kDescribedbyIds:
|
|
case ax::mojom::IntListAttribute::kFlowtoIds:
|
|
case ax::mojom::IntListAttribute::kIndirectChildIds:
|
|
case ax::mojom::IntListAttribute::kLabelledbyIds:
|
|
case ax::mojom::IntListAttribute::kRadioGroupIds:
|
|
return true;
|
|
|
|
// Note: all of the attributes are included here explicitly,
|
|
// rather than using "default:", so that it's a compiler error to
|
|
// add a new attribute without explicitly considering whether it's
|
|
// a node id attribute or not.
|
|
case ax::mojom::IntListAttribute::kNone:
|
|
case ax::mojom::IntListAttribute::kMarkerTypes:
|
|
case ax::mojom::IntListAttribute::kMarkerStarts:
|
|
case ax::mojom::IntListAttribute::kMarkerEnds:
|
|
case ax::mojom::IntListAttribute::kCharacterOffsets:
|
|
case ax::mojom::IntListAttribute::kCachedLineStarts:
|
|
case ax::mojom::IntListAttribute::kWordStarts:
|
|
case ax::mojom::IntListAttribute::kWordEnds:
|
|
case ax::mojom::IntListAttribute::kCustomActionIds:
|
|
return false;
|
|
}
|
|
|
|
NOTREACHED();
|
|
return false;
|
|
}
|
|
|
|
AXNodeData::AXNodeData()
|
|
: role(ax::mojom::Role::kUnknown), state(0U), actions(0ULL) {}
|
|
|
|
AXNodeData::~AXNodeData() = default;
|
|
|
|
AXNodeData::AXNodeData(const AXNodeData& other) {
|
|
id = other.id;
|
|
role = other.role;
|
|
state = other.state;
|
|
actions = other.actions;
|
|
string_attributes = other.string_attributes;
|
|
int_attributes = other.int_attributes;
|
|
float_attributes = other.float_attributes;
|
|
bool_attributes = other.bool_attributes;
|
|
intlist_attributes = other.intlist_attributes;
|
|
stringlist_attributes = other.stringlist_attributes;
|
|
html_attributes = other.html_attributes;
|
|
child_ids = other.child_ids;
|
|
relative_bounds = other.relative_bounds;
|
|
}
|
|
|
|
AXNodeData::AXNodeData(AXNodeData&& other) {
|
|
id = other.id;
|
|
role = other.role;
|
|
state = other.state;
|
|
actions = other.actions;
|
|
string_attributes.swap(other.string_attributes);
|
|
int_attributes.swap(other.int_attributes);
|
|
float_attributes.swap(other.float_attributes);
|
|
bool_attributes.swap(other.bool_attributes);
|
|
intlist_attributes.swap(other.intlist_attributes);
|
|
stringlist_attributes.swap(other.stringlist_attributes);
|
|
html_attributes.swap(other.html_attributes);
|
|
child_ids.swap(other.child_ids);
|
|
relative_bounds = other.relative_bounds;
|
|
}
|
|
|
|
AXNodeData& AXNodeData::operator=(AXNodeData other) {
|
|
id = other.id;
|
|
role = other.role;
|
|
state = other.state;
|
|
actions = other.actions;
|
|
string_attributes = other.string_attributes;
|
|
int_attributes = other.int_attributes;
|
|
float_attributes = other.float_attributes;
|
|
bool_attributes = other.bool_attributes;
|
|
intlist_attributes = other.intlist_attributes;
|
|
stringlist_attributes = other.stringlist_attributes;
|
|
html_attributes = other.html_attributes;
|
|
child_ids = other.child_ids;
|
|
relative_bounds = other.relative_bounds;
|
|
return *this;
|
|
}
|
|
|
|
bool AXNodeData::HasBoolAttribute(ax::mojom::BoolAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, bool_attributes);
|
|
return iter != bool_attributes.end();
|
|
}
|
|
|
|
bool AXNodeData::GetBoolAttribute(ax::mojom::BoolAttribute attribute) const {
|
|
bool result;
|
|
if (GetBoolAttribute(attribute, &result))
|
|
return result;
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::GetBoolAttribute(ax::mojom::BoolAttribute attribute,
|
|
bool* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, bool_attributes);
|
|
if (iter != bool_attributes.end()) {
|
|
*value = iter->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::HasFloatAttribute(ax::mojom::FloatAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, float_attributes);
|
|
return iter != float_attributes.end();
|
|
}
|
|
|
|
float AXNodeData::GetFloatAttribute(ax::mojom::FloatAttribute attribute) const {
|
|
float result;
|
|
if (GetFloatAttribute(attribute, &result))
|
|
return result;
|
|
return 0.0;
|
|
}
|
|
|
|
bool AXNodeData::GetFloatAttribute(ax::mojom::FloatAttribute attribute,
|
|
float* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, float_attributes);
|
|
if (iter != float_attributes.end()) {
|
|
*value = iter->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::HasIntAttribute(ax::mojom::IntAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, int_attributes);
|
|
return iter != int_attributes.end();
|
|
}
|
|
|
|
int AXNodeData::GetIntAttribute(ax::mojom::IntAttribute attribute) const {
|
|
int result;
|
|
if (GetIntAttribute(attribute, &result))
|
|
return result;
|
|
return 0;
|
|
}
|
|
|
|
bool AXNodeData::GetIntAttribute(ax::mojom::IntAttribute attribute,
|
|
int* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, int_attributes);
|
|
if (iter != int_attributes.end()) {
|
|
*value = int{iter->second};
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::HasStringAttribute(
|
|
ax::mojom::StringAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, string_attributes);
|
|
return iter != string_attributes.end();
|
|
}
|
|
|
|
const std::string& AXNodeData::GetStringAttribute(
|
|
ax::mojom::StringAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, string_attributes);
|
|
return iter != string_attributes.end() ? iter->second : base::EmptyString();
|
|
}
|
|
|
|
bool AXNodeData::GetStringAttribute(ax::mojom::StringAttribute attribute,
|
|
std::string* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, string_attributes);
|
|
if (iter != string_attributes.end()) {
|
|
*value = iter->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
base::string16 AXNodeData::GetString16Attribute(
|
|
ax::mojom::StringAttribute attribute) const {
|
|
std::string value_utf8;
|
|
if (!GetStringAttribute(attribute, &value_utf8))
|
|
return base::string16();
|
|
return base::UTF8ToUTF16(value_utf8);
|
|
}
|
|
|
|
bool AXNodeData::GetString16Attribute(ax::mojom::StringAttribute attribute,
|
|
base::string16* value) const {
|
|
std::string value_utf8;
|
|
if (!GetStringAttribute(attribute, &value_utf8))
|
|
return false;
|
|
*value = base::UTF8ToUTF16(value_utf8);
|
|
return true;
|
|
}
|
|
|
|
bool AXNodeData::HasIntListAttribute(
|
|
ax::mojom::IntListAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, intlist_attributes);
|
|
return iter != intlist_attributes.end();
|
|
}
|
|
|
|
const std::vector<int32_t>& AXNodeData::GetIntListAttribute(
|
|
ax::mojom::IntListAttribute attribute) const {
|
|
static const base::NoDestructor<std::vector<int32_t>> empty_vector;
|
|
auto iter = FindInVectorOfPairs(attribute, intlist_attributes);
|
|
if (iter != intlist_attributes.end())
|
|
return iter->second;
|
|
return *empty_vector;
|
|
}
|
|
|
|
bool AXNodeData::GetIntListAttribute(ax::mojom::IntListAttribute attribute,
|
|
std::vector<int32_t>* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, intlist_attributes);
|
|
if (iter != intlist_attributes.end()) {
|
|
*value = iter->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::HasStringListAttribute(
|
|
ax::mojom::StringListAttribute attribute) const {
|
|
auto iter = FindInVectorOfPairs(attribute, stringlist_attributes);
|
|
return iter != stringlist_attributes.end();
|
|
}
|
|
|
|
const std::vector<std::string>& AXNodeData::GetStringListAttribute(
|
|
ax::mojom::StringListAttribute attribute) const {
|
|
static const base::NoDestructor<std::vector<std::string>> empty_vector;
|
|
auto iter = FindInVectorOfPairs(attribute, stringlist_attributes);
|
|
if (iter != stringlist_attributes.end())
|
|
return iter->second;
|
|
return *empty_vector;
|
|
}
|
|
|
|
bool AXNodeData::GetStringListAttribute(
|
|
ax::mojom::StringListAttribute attribute,
|
|
std::vector<std::string>* value) const {
|
|
auto iter = FindInVectorOfPairs(attribute, stringlist_attributes);
|
|
if (iter != stringlist_attributes.end()) {
|
|
*value = iter->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::GetHtmlAttribute(const char* html_attr,
|
|
std::string* value) const {
|
|
for (const std::pair<std::string, std::string>& html_attribute :
|
|
html_attributes) {
|
|
const std::string& attr = html_attribute.first;
|
|
if (base::LowerCaseEqualsASCII(attr, html_attr)) {
|
|
*value = html_attribute.second;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::GetHtmlAttribute(const char* html_attr,
|
|
base::string16* value) const {
|
|
std::string value_utf8;
|
|
if (!GetHtmlAttribute(html_attr, &value_utf8))
|
|
return false;
|
|
*value = base::UTF8ToUTF16(value_utf8);
|
|
return true;
|
|
}
|
|
|
|
void AXNodeData::AddStringAttribute(ax::mojom::StringAttribute attribute,
|
|
const std::string& value) {
|
|
DCHECK_NE(attribute, ax::mojom::StringAttribute::kNone);
|
|
if (HasStringAttribute(attribute))
|
|
RemoveStringAttribute(attribute);
|
|
string_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::AddIntAttribute(ax::mojom::IntAttribute attribute, int value) {
|
|
DCHECK_NE(attribute, ax::mojom::IntAttribute::kNone);
|
|
if (HasIntAttribute(attribute))
|
|
RemoveIntAttribute(attribute);
|
|
int_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::AddFloatAttribute(ax::mojom::FloatAttribute attribute,
|
|
float value) {
|
|
DCHECK_NE(attribute, ax::mojom::FloatAttribute::kNone);
|
|
if (HasFloatAttribute(attribute))
|
|
RemoveFloatAttribute(attribute);
|
|
float_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::AddBoolAttribute(ax::mojom::BoolAttribute attribute,
|
|
bool value) {
|
|
DCHECK_NE(attribute, ax::mojom::BoolAttribute::kNone);
|
|
if (HasBoolAttribute(attribute))
|
|
RemoveBoolAttribute(attribute);
|
|
bool_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::AddIntListAttribute(ax::mojom::IntListAttribute attribute,
|
|
const std::vector<int32_t>& value) {
|
|
DCHECK_NE(attribute, ax::mojom::IntListAttribute::kNone);
|
|
if (HasIntListAttribute(attribute))
|
|
RemoveIntListAttribute(attribute);
|
|
intlist_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::AddStringListAttribute(
|
|
ax::mojom::StringListAttribute attribute,
|
|
const std::vector<std::string>& value) {
|
|
DCHECK_NE(attribute, ax::mojom::StringListAttribute::kNone);
|
|
if (HasStringListAttribute(attribute))
|
|
RemoveStringListAttribute(attribute);
|
|
stringlist_attributes.push_back(std::make_pair(attribute, value));
|
|
}
|
|
|
|
void AXNodeData::RemoveStringAttribute(ax::mojom::StringAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::StringAttribute::kNone);
|
|
base::EraseIf(string_attributes, [attribute](const auto& string_attribute) {
|
|
return string_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
void AXNodeData::RemoveIntAttribute(ax::mojom::IntAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::IntAttribute::kNone);
|
|
base::EraseIf(int_attributes, [attribute](const auto& int_attribute) {
|
|
return int_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
void AXNodeData::RemoveFloatAttribute(ax::mojom::FloatAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::FloatAttribute::kNone);
|
|
base::EraseIf(float_attributes, [attribute](const auto& float_attribute) {
|
|
return float_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
void AXNodeData::RemoveBoolAttribute(ax::mojom::BoolAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::BoolAttribute::kNone);
|
|
base::EraseIf(bool_attributes, [attribute](const auto& bool_attribute) {
|
|
return bool_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
void AXNodeData::RemoveIntListAttribute(ax::mojom::IntListAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::IntListAttribute::kNone);
|
|
base::EraseIf(intlist_attributes, [attribute](const auto& intlist_attribute) {
|
|
return intlist_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
void AXNodeData::RemoveStringListAttribute(
|
|
ax::mojom::StringListAttribute attribute) {
|
|
DCHECK_NE(attribute, ax::mojom::StringListAttribute::kNone);
|
|
base::EraseIf(stringlist_attributes,
|
|
[attribute](const auto& stringlist_attribute) {
|
|
return stringlist_attribute.first == attribute;
|
|
});
|
|
}
|
|
|
|
AXNodeTextStyles AXNodeData::GetTextStyles() const {
|
|
AXNodeTextStyles style_attributes;
|
|
|
|
GetIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
|
|
&style_attributes.background_color);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kColor, &style_attributes.color);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kInvalidState,
|
|
&style_attributes.invalid_state);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextOverlineStyle,
|
|
&style_attributes.overline_style);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextDirection,
|
|
&style_attributes.text_direction);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextPosition,
|
|
&style_attributes.text_position);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextStrikethroughStyle,
|
|
&style_attributes.strikethrough_style);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextStyle,
|
|
&style_attributes.text_style);
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextUnderlineStyle,
|
|
&style_attributes.underline_style);
|
|
GetFloatAttribute(ax::mojom::FloatAttribute::kFontSize,
|
|
&style_attributes.font_size);
|
|
GetFloatAttribute(ax::mojom::FloatAttribute::kFontWeight,
|
|
&style_attributes.font_weight);
|
|
GetStringAttribute(ax::mojom::StringAttribute::kFontFamily,
|
|
&style_attributes.font_family);
|
|
|
|
return style_attributes;
|
|
}
|
|
|
|
void AXNodeData::SetName(const std::string& name) {
|
|
DCHECK_NE(role, ax::mojom::Role::kNone)
|
|
<< "A valid role is required before setting the name attribute, because "
|
|
"the role is used for setting the required NameFrom attribute.";
|
|
|
|
auto iter = std::find_if(string_attributes.begin(), string_attributes.end(),
|
|
[](const auto& string_attribute) {
|
|
return string_attribute.first ==
|
|
ax::mojom::StringAttribute::kName;
|
|
});
|
|
|
|
if (iter == string_attributes.end()) {
|
|
string_attributes.push_back(
|
|
std::make_pair(ax::mojom::StringAttribute::kName, name));
|
|
} else {
|
|
iter->second = name;
|
|
}
|
|
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kNameFrom))
|
|
return;
|
|
// Since this method is mostly used by tests which don't always set the
|
|
// "NameFrom" attribute, we need to set it here to the most likely value if
|
|
// not set, otherwise code that tries to calculate the node's inner text, its
|
|
// hypertext, or even its value, might not know whether to include the name in
|
|
// the result or not.
|
|
//
|
|
// For example, if there is a text field, but it is empty, i.e. it has no
|
|
// value, its value could be its name if "NameFrom" is set to "kPlaceholder"
|
|
// or to "kContents" but not if it's set to "kAttribute". Similarly, if there
|
|
// is a button without any unignored children, it's name can only be
|
|
// equivalent to its inner text if "NameFrom" is set to "kContents" or to
|
|
// "kValue", but not if it is set to "kAttribute".
|
|
if (IsText(role)) {
|
|
SetNameFrom(ax::mojom::NameFrom::kContents);
|
|
} else {
|
|
SetNameFrom(ax::mojom::NameFrom::kAttribute);
|
|
}
|
|
}
|
|
|
|
void AXNodeData::SetName(const base::string16& name) {
|
|
SetName(base::UTF16ToUTF8(name));
|
|
}
|
|
|
|
void AXNodeData::SetNameExplicitlyEmpty() {
|
|
SetNameFrom(ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
|
|
}
|
|
|
|
void AXNodeData::SetDescription(const std::string& description) {
|
|
AddStringAttribute(ax::mojom::StringAttribute::kDescription, description);
|
|
}
|
|
|
|
void AXNodeData::SetDescription(const base::string16& description) {
|
|
SetDescription(base::UTF16ToUTF8(description));
|
|
}
|
|
|
|
void AXNodeData::SetValue(const std::string& value) {
|
|
AddStringAttribute(ax::mojom::StringAttribute::kValue, value);
|
|
}
|
|
|
|
void AXNodeData::SetValue(const base::string16& value) {
|
|
SetValue(base::UTF16ToUTF8(value));
|
|
}
|
|
|
|
bool AXNodeData::HasState(ax::mojom::State state_enum) const {
|
|
return IsFlagSet(state, static_cast<uint32_t>(state_enum));
|
|
}
|
|
|
|
bool AXNodeData::HasAction(ax::mojom::Action action) const {
|
|
return IsFlagSet(actions, static_cast<uint32_t>(action));
|
|
}
|
|
|
|
bool AXNodeData::HasTextStyle(ax::mojom::TextStyle text_style_enum) const {
|
|
int32_t style = GetIntAttribute(ax::mojom::IntAttribute::kTextStyle);
|
|
return IsFlagSet(static_cast<uint32_t>(style),
|
|
static_cast<uint32_t>(text_style_enum));
|
|
}
|
|
|
|
bool AXNodeData::HasDropeffect(ax::mojom::Dropeffect dropeffect_enum) const {
|
|
int32_t dropeffect = GetIntAttribute(ax::mojom::IntAttribute::kDropeffect);
|
|
return IsFlagSet(static_cast<uint32_t>(dropeffect),
|
|
static_cast<uint32_t>(dropeffect_enum));
|
|
}
|
|
|
|
void AXNodeData::AddState(ax::mojom::State state_enum) {
|
|
DCHECK_GT(static_cast<int>(state_enum),
|
|
static_cast<int>(ax::mojom::State::kNone));
|
|
DCHECK_LE(static_cast<int>(state_enum),
|
|
static_cast<int>(ax::mojom::State::kMaxValue));
|
|
state = ModifyFlag(state, static_cast<uint32_t>(state_enum), true);
|
|
}
|
|
|
|
void AXNodeData::RemoveState(ax::mojom::State state_enum) {
|
|
DCHECK_GT(static_cast<int>(state_enum),
|
|
static_cast<int>(ax::mojom::State::kNone));
|
|
DCHECK_LE(static_cast<int>(state_enum),
|
|
static_cast<int>(ax::mojom::State::kMaxValue));
|
|
state = ModifyFlag(state, static_cast<uint32_t>(state_enum), false);
|
|
}
|
|
|
|
void AXNodeData::AddAction(ax::mojom::Action action_enum) {
|
|
switch (action_enum) {
|
|
case ax::mojom::Action::kNone:
|
|
NOTREACHED();
|
|
break;
|
|
|
|
// Note: all of the attributes are included here explicitly, rather than
|
|
// using "default:", so that it's a compiler error to add a new action
|
|
// without explicitly considering whether there are mutually exclusive
|
|
// actions that can be performed on a UI control at the same time.
|
|
case ax::mojom::Action::kBlur:
|
|
case ax::mojom::Action::kFocus: {
|
|
ax::mojom::Action excluded_action =
|
|
(action_enum == ax::mojom::Action::kBlur) ? ax::mojom::Action::kFocus
|
|
: ax::mojom::Action::kBlur;
|
|
DCHECK(!HasAction(excluded_action)) << excluded_action;
|
|
break;
|
|
}
|
|
|
|
case ax::mojom::Action::kClearAccessibilityFocus:
|
|
case ax::mojom::Action::kCollapse:
|
|
case ax::mojom::Action::kCustomAction:
|
|
case ax::mojom::Action::kDecrement:
|
|
case ax::mojom::Action::kDoDefault:
|
|
case ax::mojom::Action::kExpand:
|
|
case ax::mojom::Action::kGetImageData:
|
|
case ax::mojom::Action::kHitTest:
|
|
case ax::mojom::Action::kIncrement:
|
|
case ax::mojom::Action::kInternalInvalidateTree:
|
|
case ax::mojom::Action::kLoadInlineTextBoxes:
|
|
case ax::mojom::Action::kReplaceSelectedText:
|
|
case ax::mojom::Action::kScrollToMakeVisible:
|
|
case ax::mojom::Action::kScrollToPoint:
|
|
case ax::mojom::Action::kSetAccessibilityFocus:
|
|
case ax::mojom::Action::kSetScrollOffset:
|
|
case ax::mojom::Action::kSetSelection:
|
|
case ax::mojom::Action::kSetSequentialFocusNavigationStartingPoint:
|
|
case ax::mojom::Action::kSetValue:
|
|
case ax::mojom::Action::kShowContextMenu:
|
|
case ax::mojom::Action::kScrollBackward:
|
|
case ax::mojom::Action::kScrollForward:
|
|
case ax::mojom::Action::kScrollUp:
|
|
case ax::mojom::Action::kScrollDown:
|
|
case ax::mojom::Action::kScrollLeft:
|
|
case ax::mojom::Action::kScrollRight:
|
|
case ax::mojom::Action::kGetTextLocation:
|
|
case ax::mojom::Action::kAnnotatePageImages:
|
|
case ax::mojom::Action::kSignalEndOfTest:
|
|
case ax::mojom::Action::kHideTooltip:
|
|
case ax::mojom::Action::kShowTooltip:
|
|
break;
|
|
}
|
|
|
|
actions = ModifyFlag(actions, static_cast<uint32_t>(action_enum), true);
|
|
}
|
|
|
|
void AXNodeData::AddTextStyle(ax::mojom::TextStyle text_style_enum) {
|
|
DCHECK_GE(static_cast<int>(text_style_enum),
|
|
static_cast<int>(ax::mojom::TextStyle::kMinValue));
|
|
DCHECK_LE(static_cast<int>(text_style_enum),
|
|
static_cast<int>(ax::mojom::TextStyle::kMaxValue));
|
|
int32_t style = GetIntAttribute(ax::mojom::IntAttribute::kTextStyle);
|
|
style = ModifyFlag(static_cast<uint32_t>(style),
|
|
static_cast<uint32_t>(text_style_enum), true);
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kTextStyle);
|
|
AddIntAttribute(ax::mojom::IntAttribute::kTextStyle, style);
|
|
}
|
|
|
|
void AXNodeData::AddDropeffect(ax::mojom::Dropeffect dropeffect_enum) {
|
|
DCHECK_GE(static_cast<int>(dropeffect_enum),
|
|
static_cast<int>(ax::mojom::Dropeffect::kMinValue));
|
|
DCHECK_LE(static_cast<int>(dropeffect_enum),
|
|
static_cast<int>(ax::mojom::Dropeffect::kMaxValue));
|
|
int32_t dropeffect = GetIntAttribute(ax::mojom::IntAttribute::kDropeffect);
|
|
dropeffect = ModifyFlag(static_cast<uint32_t>(dropeffect),
|
|
static_cast<uint32_t>(dropeffect_enum), true);
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kDropeffect);
|
|
AddIntAttribute(ax::mojom::IntAttribute::kDropeffect, dropeffect);
|
|
}
|
|
|
|
ax::mojom::CheckedState AXNodeData::GetCheckedState() const {
|
|
return static_cast<ax::mojom::CheckedState>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
|
|
}
|
|
|
|
void AXNodeData::SetCheckedState(ax::mojom::CheckedState checked_state) {
|
|
if (HasCheckedState())
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kCheckedState);
|
|
if (checked_state != ax::mojom::CheckedState::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kCheckedState,
|
|
static_cast<int32_t>(checked_state));
|
|
}
|
|
}
|
|
|
|
bool AXNodeData::HasCheckedState() const {
|
|
return HasIntAttribute(ax::mojom::IntAttribute::kCheckedState);
|
|
}
|
|
|
|
ax::mojom::DefaultActionVerb AXNodeData::GetDefaultActionVerb() const {
|
|
return static_cast<ax::mojom::DefaultActionVerb>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb));
|
|
}
|
|
|
|
void AXNodeData::SetDefaultActionVerb(
|
|
ax::mojom::DefaultActionVerb default_action_verb) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb);
|
|
if (default_action_verb != ax::mojom::DefaultActionVerb::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb,
|
|
static_cast<int32_t>(default_action_verb));
|
|
}
|
|
}
|
|
|
|
ax::mojom::HasPopup AXNodeData::GetHasPopup() const {
|
|
return static_cast<ax::mojom::HasPopup>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kHasPopup));
|
|
}
|
|
|
|
void AXNodeData::SetHasPopup(ax::mojom::HasPopup has_popup) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kHasPopup))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kHasPopup);
|
|
if (has_popup != ax::mojom::HasPopup::kFalse) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kHasPopup,
|
|
static_cast<int32_t>(has_popup));
|
|
}
|
|
}
|
|
|
|
ax::mojom::InvalidState AXNodeData::GetInvalidState() const {
|
|
return static_cast<ax::mojom::InvalidState>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kInvalidState));
|
|
}
|
|
|
|
void AXNodeData::SetInvalidState(ax::mojom::InvalidState invalid_state) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kInvalidState))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kInvalidState);
|
|
if (invalid_state != ax::mojom::InvalidState::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kInvalidState,
|
|
static_cast<int32_t>(invalid_state));
|
|
}
|
|
}
|
|
|
|
ax::mojom::NameFrom AXNodeData::GetNameFrom() const {
|
|
return static_cast<ax::mojom::NameFrom>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
|
|
}
|
|
|
|
void AXNodeData::SetNameFrom(ax::mojom::NameFrom name_from) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kNameFrom))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kNameFrom);
|
|
if (name_from != ax::mojom::NameFrom::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kNameFrom,
|
|
static_cast<int32_t>(name_from));
|
|
}
|
|
}
|
|
|
|
ax::mojom::DescriptionFrom AXNodeData::GetDescriptionFrom() const {
|
|
return static_cast<ax::mojom::DescriptionFrom>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kDescriptionFrom));
|
|
}
|
|
|
|
void AXNodeData::SetDescriptionFrom(
|
|
ax::mojom::DescriptionFrom description_from) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kDescriptionFrom))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kDescriptionFrom);
|
|
if (description_from != ax::mojom::DescriptionFrom::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kDescriptionFrom,
|
|
static_cast<int32_t>(description_from));
|
|
}
|
|
}
|
|
|
|
ax::mojom::TextPosition AXNodeData::GetTextPosition() const {
|
|
return static_cast<ax::mojom::TextPosition>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextPosition));
|
|
}
|
|
|
|
void AXNodeData::SetTextPosition(ax::mojom::TextPosition text_position) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kTextPosition))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kTextPosition);
|
|
if (text_position != ax::mojom::TextPosition::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kTextPosition,
|
|
static_cast<int32_t>(text_position));
|
|
}
|
|
}
|
|
|
|
ax::mojom::ImageAnnotationStatus AXNodeData::GetImageAnnotationStatus() const {
|
|
return static_cast<ax::mojom::ImageAnnotationStatus>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kImageAnnotationStatus));
|
|
}
|
|
|
|
void AXNodeData::SetImageAnnotationStatus(
|
|
ax::mojom::ImageAnnotationStatus status) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kImageAnnotationStatus))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kImageAnnotationStatus);
|
|
if (status != ax::mojom::ImageAnnotationStatus::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kImageAnnotationStatus,
|
|
static_cast<int32_t>(status));
|
|
}
|
|
}
|
|
|
|
ax::mojom::Restriction AXNodeData::GetRestriction() const {
|
|
return static_cast<ax::mojom::Restriction>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kRestriction));
|
|
}
|
|
|
|
void AXNodeData::SetRestriction(ax::mojom::Restriction restriction) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kRestriction))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kRestriction);
|
|
if (restriction != ax::mojom::Restriction::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kRestriction,
|
|
static_cast<int32_t>(restriction));
|
|
}
|
|
}
|
|
|
|
ax::mojom::ListStyle AXNodeData::GetListStyle() const {
|
|
return static_cast<ax::mojom::ListStyle>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kListStyle));
|
|
}
|
|
|
|
void AXNodeData::SetListStyle(ax::mojom::ListStyle list_style) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kListStyle))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kListStyle);
|
|
if (list_style != ax::mojom::ListStyle::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kListStyle,
|
|
static_cast<int32_t>(list_style));
|
|
}
|
|
}
|
|
|
|
ax::mojom::TextAlign AXNodeData::GetTextAlign() const {
|
|
return static_cast<ax::mojom::TextAlign>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextAlign));
|
|
}
|
|
|
|
void AXNodeData::SetTextAlign(ax::mojom::TextAlign text_align) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kTextAlign))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kTextAlign);
|
|
AddIntAttribute(ax::mojom::IntAttribute::kTextAlign,
|
|
static_cast<int32_t>(text_align));
|
|
}
|
|
|
|
ax::mojom::WritingDirection AXNodeData::GetTextDirection() const {
|
|
return static_cast<ax::mojom::WritingDirection>(
|
|
GetIntAttribute(ax::mojom::IntAttribute::kTextDirection));
|
|
}
|
|
|
|
void AXNodeData::SetTextDirection(ax::mojom::WritingDirection text_direction) {
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kTextDirection))
|
|
RemoveIntAttribute(ax::mojom::IntAttribute::kTextDirection);
|
|
if (text_direction != ax::mojom::WritingDirection::kNone) {
|
|
AddIntAttribute(ax::mojom::IntAttribute::kTextDirection,
|
|
static_cast<int32_t>(text_direction));
|
|
}
|
|
}
|
|
|
|
bool AXNodeData::IsActivatable() const {
|
|
return IsTextField() || role == ax::mojom::Role::kListBox;
|
|
}
|
|
|
|
bool AXNodeData::IsButtonPressed() const {
|
|
// Currently there is no internal representation for |aria-pressed|, and
|
|
// we map |aria-pressed="true"| to ax::mojom::CheckedState::kTrue for a native
|
|
// button or role="button".
|
|
// https://www.w3.org/TR/wai-aria-1.1/#aria-pressed
|
|
if (IsButton(role) && GetCheckedState() == ax::mojom::CheckedState::kTrue)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::IsClickable() const {
|
|
// If it has a custom default action verb except for
|
|
// ax::mojom::DefaultActionVerb::kClickAncestor, it's definitely clickable.
|
|
// ax::mojom::DefaultActionVerb::kClickAncestor is used when an element with a
|
|
// click listener is present in its ancestry chain.
|
|
if (HasIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb) &&
|
|
(GetDefaultActionVerb() != ax::mojom::DefaultActionVerb::kClickAncestor))
|
|
return true;
|
|
|
|
return ui::IsClickable(role);
|
|
}
|
|
|
|
bool AXNodeData::IsSelectable() const {
|
|
// It's selectable if it has the attribute, whether it's true or false.
|
|
return HasBoolAttribute(ax::mojom::BoolAttribute::kSelected) &&
|
|
GetRestriction() != ax::mojom::Restriction::kDisabled;
|
|
}
|
|
|
|
bool AXNodeData::IsIgnored() const {
|
|
return HasState(ax::mojom::State::kIgnored) ||
|
|
role == ax::mojom::Role::kIgnored;
|
|
}
|
|
|
|
bool AXNodeData::IsInvisibleOrIgnored() const {
|
|
return IsIgnored() || HasState(ax::mojom::State::kInvisible);
|
|
}
|
|
|
|
bool AXNodeData::IsInvocable() const {
|
|
// A control is "invocable" if it initiates an action when activated but
|
|
// does not maintain any state. A control that maintains state when activated
|
|
// would be considered a toggle or expand-collapse element - these elements
|
|
// are "clickable" but not "invocable". Similarly, if the action only involves
|
|
// activating the control, such as when clicking a text field, the control is
|
|
// not considered "invocable".
|
|
return IsClickable() && !IsActivatable() && !SupportsExpandCollapse() &&
|
|
!SupportsToggle(role);
|
|
}
|
|
|
|
bool AXNodeData::IsMenuButton() const {
|
|
// According to the WAI-ARIA spec, a menu button is a native button or an ARIA
|
|
// role="button" that opens a menu. Although ARIA does not include a role
|
|
// specifically for menu buttons, screen readers identify buttons that have
|
|
// aria-haspopup="true" or aria-haspopup="menu" as menu buttons, and Blink
|
|
// maps both to HasPopup::kMenu.
|
|
// https://www.w3.org/TR/wai-aria-practices/#menubutton
|
|
// https://www.w3.org/TR/wai-aria-1.1/#aria-haspopup
|
|
if (IsButton(role) && GetHasPopup() == ax::mojom::HasPopup::kMenu)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AXNodeData::IsTextField() const {
|
|
return IsPlainTextField() || IsRichTextField();
|
|
}
|
|
|
|
bool AXNodeData::IsPasswordField() const {
|
|
return IsTextField() && HasState(ax::mojom::State::kProtected);
|
|
}
|
|
|
|
bool AXNodeData::IsPlainTextField() const {
|
|
// We need to check both the role and editable state, because some ARIA text
|
|
// fields may in fact not be editable, whilst some editable fields might not
|
|
// have the role.
|
|
return !HasState(ax::mojom::State::kRichlyEditable) &&
|
|
(role == ax::mojom::Role::kTextField ||
|
|
role == ax::mojom::Role::kTextFieldWithComboBox ||
|
|
role == ax::mojom::Role::kSearchBox ||
|
|
GetBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot));
|
|
}
|
|
|
|
bool AXNodeData::IsRichTextField() const {
|
|
return GetBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot) &&
|
|
HasState(ax::mojom::State::kRichlyEditable);
|
|
}
|
|
|
|
bool AXNodeData::IsReadOnlyOrDisabled() const {
|
|
switch (GetRestriction()) {
|
|
case ax::mojom::Restriction::kReadOnly:
|
|
case ax::mojom::Restriction::kDisabled:
|
|
return true;
|
|
case ax::mojom::Restriction::kNone: {
|
|
if (HasState(ax::mojom::State::kEditable) ||
|
|
HasState(ax::mojom::State::kRichlyEditable)) {
|
|
return false;
|
|
}
|
|
|
|
// By default, when readonly is not supported, we assume the node is never
|
|
// editable - then always readonly.
|
|
return ShouldHaveReadonlyStateByDefault(role) ||
|
|
!IsReadOnlySupported(role);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AXNodeData::IsRangeValueSupported() const {
|
|
if (role == ax::mojom::Role::kSplitter) {
|
|
// According to the ARIA spec, role="separator" acts as a splitter only
|
|
// when focusable, and supports a range only in that case.
|
|
return HasState(ax::mojom::State::kFocusable);
|
|
}
|
|
return ui::IsRangeValueSupported(role);
|
|
}
|
|
|
|
bool AXNodeData::SupportsExpandCollapse() const {
|
|
if (GetHasPopup() != ax::mojom::HasPopup::kFalse ||
|
|
HasState(ax::mojom::State::kExpanded) ||
|
|
HasState(ax::mojom::State::kCollapsed))
|
|
return true;
|
|
|
|
return ui::SupportsExpandCollapse(role);
|
|
}
|
|
|
|
std::string AXNodeData::ToString() const {
|
|
std::string result;
|
|
|
|
result += "id=" + base::NumberToString(id);
|
|
result += " ";
|
|
result += ui::ToString(role);
|
|
|
|
result += StateBitfieldToString(state);
|
|
|
|
result += " " + relative_bounds.ToString();
|
|
|
|
for (const std::pair<ax::mojom::IntAttribute, int32_t>& int_attribute :
|
|
int_attributes) {
|
|
std::string value = base::NumberToString(int_attribute.second);
|
|
switch (int_attribute.first) {
|
|
case ax::mojom::IntAttribute::kDefaultActionVerb:
|
|
result += std::string(" action=") +
|
|
ui::ToString(static_cast<ax::mojom::DefaultActionVerb>(
|
|
int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollX:
|
|
result += " scroll_x=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollXMin:
|
|
result += " scroll_x_min=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollXMax:
|
|
result += " scroll_x_max=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollY:
|
|
result += " scroll_y=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollYMin:
|
|
result += " scroll_y_min=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kScrollYMax:
|
|
result += " scroll_y_max=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kHierarchicalLevel:
|
|
result += " level=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextSelStart:
|
|
result += " sel_start=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextSelEnd:
|
|
result += " sel_end=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaColumnCount:
|
|
result += " aria_column_count=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaCellColumnIndex:
|
|
result += " aria_cell_column_index=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaCellColumnSpan:
|
|
result += " aria_cell_column_span=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaRowCount:
|
|
result += " aria_row_count=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaCellRowIndex:
|
|
result += " aria_cell_row_index=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaCellRowSpan:
|
|
result += " aria_cell_row_span=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableRowCount:
|
|
result += " rows=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableColumnCount:
|
|
result += " cols=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableCellColumnIndex:
|
|
result += " col=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableCellRowIndex:
|
|
result += " row=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableCellColumnSpan:
|
|
result += " colspan=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableCellRowSpan:
|
|
result += " rowspan=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableColumnHeaderId:
|
|
result += " column_header_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableColumnIndex:
|
|
result += " column_index=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableHeaderId:
|
|
result += " header_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableRowHeaderId:
|
|
result += " row_header_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kTableRowIndex:
|
|
result += " row_index=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kSortDirection:
|
|
switch (static_cast<ax::mojom::SortDirection>(int_attribute.second)) {
|
|
case ax::mojom::SortDirection::kUnsorted:
|
|
result += " sort_direction=none";
|
|
break;
|
|
case ax::mojom::SortDirection::kAscending:
|
|
result += " sort_direction=ascending";
|
|
break;
|
|
case ax::mojom::SortDirection::kDescending:
|
|
result += " sort_direction=descending";
|
|
break;
|
|
case ax::mojom::SortDirection::kOther:
|
|
result += " sort_direction=other";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kNameFrom:
|
|
result += " name_from=";
|
|
result += ui::ToString(
|
|
static_cast<ax::mojom::NameFrom>(int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kDescriptionFrom:
|
|
result += " description_from=";
|
|
result += ui::ToString(
|
|
static_cast<ax::mojom::DescriptionFrom>(int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kActivedescendantId:
|
|
result += " activedescendant=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kErrormessageId:
|
|
result += " errormessage=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kInPageLinkTargetId:
|
|
result += " in_page_link_target_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kMemberOfId:
|
|
result += " member_of_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kNextOnLineId:
|
|
result += " next_on_line_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kPopupForId:
|
|
result += " popup_for_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kPreviousOnLineId:
|
|
result += " previous_on_line_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kColorValue:
|
|
result += base::StringPrintf(" color_value=&%X", int_attribute.second);
|
|
break;
|
|
case ax::mojom::IntAttribute::kAriaCurrentState:
|
|
switch (
|
|
static_cast<ax::mojom::AriaCurrentState>(int_attribute.second)) {
|
|
case ax::mojom::AriaCurrentState::kFalse:
|
|
result += " aria_current_state=false";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kTrue:
|
|
result += " aria_current_state=true";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kPage:
|
|
result += " aria_current_state=page";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kStep:
|
|
result += " aria_current_state=step";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kLocation:
|
|
result += " aria_current_state=location";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kDate:
|
|
result += " aria_current_state=date";
|
|
break;
|
|
case ax::mojom::AriaCurrentState::kTime:
|
|
result += " aria_current_state=time";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kBackgroundColor:
|
|
result +=
|
|
base::StringPrintf(" background_color=&%X", int_attribute.second);
|
|
break;
|
|
case ax::mojom::IntAttribute::kColor:
|
|
result += base::StringPrintf(" color=&%X", int_attribute.second);
|
|
break;
|
|
case ax::mojom::IntAttribute::kListStyle:
|
|
switch (static_cast<ax::mojom::ListStyle>(int_attribute.second)) {
|
|
case ax::mojom::ListStyle::kCircle:
|
|
result += " list_style=circle";
|
|
break;
|
|
case ax::mojom::ListStyle::kDisc:
|
|
result += " list_style=disc";
|
|
break;
|
|
case ax::mojom::ListStyle::kImage:
|
|
result += " list_style=image";
|
|
break;
|
|
case ax::mojom::ListStyle::kNumeric:
|
|
result += " list_style=numeric";
|
|
break;
|
|
case ax::mojom::ListStyle::kOther:
|
|
result += " list_style=other";
|
|
break;
|
|
case ax::mojom::ListStyle::kSquare:
|
|
result += " list_style=square";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextAlign:
|
|
result += " text_align=";
|
|
result += ui::ToString(
|
|
static_cast<ax::mojom::TextAlign>(int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextDirection:
|
|
switch (
|
|
static_cast<ax::mojom::WritingDirection>(int_attribute.second)) {
|
|
case ax::mojom::WritingDirection::kLtr:
|
|
result += " text_direction=ltr";
|
|
break;
|
|
case ax::mojom::WritingDirection::kRtl:
|
|
result += " text_direction=rtl";
|
|
break;
|
|
case ax::mojom::WritingDirection::kTtb:
|
|
result += " text_direction=ttb";
|
|
break;
|
|
case ax::mojom::WritingDirection::kBtt:
|
|
result += " text_direction=btt";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextPosition:
|
|
switch (static_cast<ax::mojom::TextPosition>(int_attribute.second)) {
|
|
case ax::mojom::TextPosition::kNone:
|
|
result += " text_position=none";
|
|
break;
|
|
case ax::mojom::TextPosition::kSubscript:
|
|
result += " text_position=subscript";
|
|
break;
|
|
case ax::mojom::TextPosition::kSuperscript:
|
|
result += " text_position=superscript";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextStyle: {
|
|
std::string text_style_value;
|
|
if (HasTextStyle(ax::mojom::TextStyle::kBold))
|
|
text_style_value += "bold,";
|
|
if (HasTextStyle(ax::mojom::TextStyle::kItalic))
|
|
text_style_value += "italic,";
|
|
if (HasTextStyle(ax::mojom::TextStyle::kUnderline))
|
|
text_style_value += "underline,";
|
|
if (HasTextStyle(ax::mojom::TextStyle::kLineThrough))
|
|
text_style_value += "line-through,";
|
|
if (HasTextStyle(ax::mojom::TextStyle::kOverline))
|
|
text_style_value += "overline,";
|
|
result += text_style_value.substr(0, text_style_value.size() - 1);
|
|
break;
|
|
}
|
|
case ax::mojom::IntAttribute::kTextOverlineStyle:
|
|
result += std::string(" text_overline_style=") +
|
|
ui::ToString(static_cast<ax::mojom::TextDecorationStyle>(
|
|
int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextStrikethroughStyle:
|
|
result += std::string(" text_strikethrough_style=") +
|
|
ui::ToString(static_cast<ax::mojom::TextDecorationStyle>(
|
|
int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kTextUnderlineStyle:
|
|
result += std::string(" text_underline_style=") +
|
|
ui::ToString(static_cast<ax::mojom::TextDecorationStyle>(
|
|
int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kSetSize:
|
|
result += " setsize=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kPosInSet:
|
|
result += " posinset=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kHasPopup:
|
|
switch (static_cast<ax::mojom::HasPopup>(int_attribute.second)) {
|
|
case ax::mojom::HasPopup::kTrue:
|
|
result += " haspopup=true";
|
|
break;
|
|
case ax::mojom::HasPopup::kMenu:
|
|
result += " haspopup=menu";
|
|
break;
|
|
case ax::mojom::HasPopup::kListbox:
|
|
result += " haspopup=listbox";
|
|
break;
|
|
case ax::mojom::HasPopup::kTree:
|
|
result += " haspopup=tree";
|
|
break;
|
|
case ax::mojom::HasPopup::kGrid:
|
|
result += " haspopup=grid";
|
|
break;
|
|
case ax::mojom::HasPopup::kDialog:
|
|
result += " haspopup=dialog";
|
|
break;
|
|
case ax::mojom::HasPopup::kFalse:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kInvalidState:
|
|
switch (static_cast<ax::mojom::InvalidState>(int_attribute.second)) {
|
|
case ax::mojom::InvalidState::kFalse:
|
|
result += " invalid_state=false";
|
|
break;
|
|
case ax::mojom::InvalidState::kTrue:
|
|
result += " invalid_state=true";
|
|
break;
|
|
case ax::mojom::InvalidState::kOther:
|
|
result += " invalid_state=other";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kCheckedState:
|
|
switch (static_cast<ax::mojom::CheckedState>(int_attribute.second)) {
|
|
case ax::mojom::CheckedState::kFalse:
|
|
result += " checked_state=false";
|
|
break;
|
|
case ax::mojom::CheckedState::kTrue:
|
|
result += " checked_state=true";
|
|
break;
|
|
case ax::mojom::CheckedState::kMixed:
|
|
result += " checked_state=mixed";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kRestriction:
|
|
switch (static_cast<ax::mojom::Restriction>(int_attribute.second)) {
|
|
case ax::mojom::Restriction::kReadOnly:
|
|
result += " restriction=readonly";
|
|
break;
|
|
case ax::mojom::Restriction::kDisabled:
|
|
result += " restriction=disabled";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ax::mojom::IntAttribute::kNextFocusId:
|
|
result += " next_focus_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kPreviousFocusId:
|
|
result += " previous_focus_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kImageAnnotationStatus:
|
|
result += std::string(" image_annotation_status=") +
|
|
ui::ToString(static_cast<ax::mojom::ImageAnnotationStatus>(
|
|
int_attribute.second));
|
|
break;
|
|
case ax::mojom::IntAttribute::kDropeffect:
|
|
result += " dropeffect=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kDOMNodeId:
|
|
result += " dom_node_id=" + value;
|
|
break;
|
|
case ax::mojom::IntAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<ax::mojom::StringAttribute, std::string>&
|
|
string_attribute : string_attributes) {
|
|
std::string value = string_attribute.second;
|
|
switch (string_attribute.first) {
|
|
case ax::mojom::StringAttribute::kAccessKey:
|
|
result += " access_key=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kAriaInvalidValue:
|
|
result += " aria_invalid_value=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kAutoComplete:
|
|
result += " autocomplete=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kChildTreeId:
|
|
result += " child_tree_id=" + value.substr(0, 8);
|
|
break;
|
|
case ax::mojom::StringAttribute::kClassName:
|
|
result += " class_name=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kDescription:
|
|
result += " description=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kDisplay:
|
|
result += " display=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kFontFamily:
|
|
result += " font-family=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kHtmlTag:
|
|
result += " html_tag=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kImageAnnotation:
|
|
result += " image_annotation=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kImageDataUrl:
|
|
result += " image_data_url=(" +
|
|
base::NumberToString(static_cast<int>(value.size())) +
|
|
" bytes)";
|
|
break;
|
|
case ax::mojom::StringAttribute::kInnerHtml:
|
|
result += " inner_html=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kInputType:
|
|
result += " input_type=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kKeyShortcuts:
|
|
result += " key_shortcuts=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kLanguage:
|
|
result += " language=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kLiveRelevant:
|
|
result += " relevant=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kLiveStatus:
|
|
result += " live=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kContainerLiveRelevant:
|
|
result += " container_relevant=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kContainerLiveStatus:
|
|
result += " container_live=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kPlaceholder:
|
|
result += " placeholder=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kRole:
|
|
result += " role=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kRoleDescription:
|
|
result += " role_description=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kTooltip:
|
|
result += " tooltip=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kUrl:
|
|
result += " url=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kName:
|
|
result += " name=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kValue:
|
|
result += " value=" + value;
|
|
break;
|
|
case ax::mojom::StringAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<ax::mojom::FloatAttribute, float>& float_attribute :
|
|
float_attributes) {
|
|
std::string value = base::NumberToString(float_attribute.second);
|
|
switch (float_attribute.first) {
|
|
case ax::mojom::FloatAttribute::kValueForRange:
|
|
result += " value_for_range=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kMaxValueForRange:
|
|
result += " max_value=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kMinValueForRange:
|
|
result += " min_value=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kStepValueForRange:
|
|
result += " step_value=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kFontSize:
|
|
result += " font_size=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kFontWeight:
|
|
result += " font_weight=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kTextIndent:
|
|
result += " text_indent=" + value;
|
|
break;
|
|
case ax::mojom::FloatAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<ax::mojom::BoolAttribute, bool>& bool_attribute :
|
|
bool_attributes) {
|
|
std::string value = bool_attribute.second ? "true" : "false";
|
|
switch (bool_attribute.first) {
|
|
case ax::mojom::BoolAttribute::kEditableRoot:
|
|
result += " editable_root=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kLiveAtomic:
|
|
result += " atomic=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kBusy:
|
|
result += " busy=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kContainerLiveAtomic:
|
|
result += " container_atomic=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kContainerLiveBusy:
|
|
result += " container_busy=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kUpdateLocationOnly:
|
|
result += " update_location_only=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kCanvasHasFallback:
|
|
result += " has_fallback=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kModal:
|
|
result += " modal=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kScrollable:
|
|
result += " scrollable=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kClickable:
|
|
result += " clickable=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kClipsChildren:
|
|
result += " clips_children=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kNotUserSelectableStyle:
|
|
result += " not_user_selectable=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kSelected:
|
|
result += " selected=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kSelectedFromFocus:
|
|
result += " selected_from_focus=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kSupportsTextLocation:
|
|
result += " supports_text_location=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kGrabbed:
|
|
result += " grabbed=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kIsLineBreakingObject:
|
|
result += " is_line_breaking_object=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kIsPageBreakingObject:
|
|
result += " is_page_breaking_object=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kHasAriaAttribute:
|
|
result += " has_aria_attribute=" + value;
|
|
break;
|
|
case ax::mojom::BoolAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<ax::mojom::IntListAttribute, std::vector<int32_t>>&
|
|
intlist_attribute : intlist_attributes) {
|
|
const std::vector<int32_t>& values = intlist_attribute.second;
|
|
switch (intlist_attribute.first) {
|
|
case ax::mojom::IntListAttribute::kIndirectChildIds:
|
|
result += " indirect_child_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kControlsIds:
|
|
result += " controls_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kDescribedbyIds:
|
|
result += " describedby_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kDetailsIds:
|
|
result += " details_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kFlowtoIds:
|
|
result += " flowto_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kLabelledbyIds:
|
|
result += " labelledby_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kRadioGroupIds:
|
|
result += " radio_group_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kMarkerTypes: {
|
|
std::string types_str;
|
|
for (size_t i = 0; i < values.size(); ++i) {
|
|
int32_t type = values[i];
|
|
if (type == static_cast<int32_t>(ax::mojom::MarkerType::kNone))
|
|
continue;
|
|
|
|
if (i > 0)
|
|
types_str += ',';
|
|
|
|
if (type & static_cast<int32_t>(ax::mojom::MarkerType::kSpelling))
|
|
types_str += "spelling&";
|
|
if (type & static_cast<int32_t>(ax::mojom::MarkerType::kGrammar))
|
|
types_str += "grammar&";
|
|
if (type & static_cast<int32_t>(ax::mojom::MarkerType::kTextMatch))
|
|
types_str += "text_match&";
|
|
if (type &
|
|
static_cast<int32_t>(ax::mojom::MarkerType::kActiveSuggestion))
|
|
types_str += "active_suggestion&";
|
|
if (type & static_cast<int32_t>(ax::mojom::MarkerType::kSuggestion))
|
|
types_str += "suggestion&";
|
|
|
|
if (!types_str.empty())
|
|
types_str = types_str.substr(0, types_str.size() - 1);
|
|
}
|
|
|
|
if (!types_str.empty())
|
|
result += " marker_types=" + types_str;
|
|
break;
|
|
}
|
|
case ax::mojom::IntListAttribute::kMarkerStarts:
|
|
result += " marker_starts=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kMarkerEnds:
|
|
result += " marker_ends=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kCharacterOffsets:
|
|
result += " character_offsets=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kCachedLineStarts:
|
|
result += " cached_line_start_offsets=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kWordStarts:
|
|
result += " word_starts=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kWordEnds:
|
|
result += " word_ends=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kCustomActionIds:
|
|
result += " custom_action_ids=" + IntVectorToString(values);
|
|
break;
|
|
case ax::mojom::IntListAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<ax::mojom::StringListAttribute,
|
|
std::vector<std::string>>& stringlist_attribute :
|
|
stringlist_attributes) {
|
|
const std::vector<std::string>& values = stringlist_attribute.second;
|
|
switch (stringlist_attribute.first) {
|
|
case ax::mojom::StringListAttribute::kCustomActionDescriptions:
|
|
result +=
|
|
" custom_action_descriptions: " + base::JoinString(values, ",");
|
|
break;
|
|
case ax::mojom::StringListAttribute::kNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (actions)
|
|
result += " actions=" + ActionsBitfieldToString(actions);
|
|
|
|
if (!child_ids.empty())
|
|
result += " child_ids=" + IntVectorToString(child_ids);
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string AXNodeData::DropeffectBitfieldToString() const {
|
|
if (!HasIntAttribute(ax::mojom::IntAttribute::kDropeffect))
|
|
return "";
|
|
|
|
std::string str;
|
|
for (int dropeffect_idx = static_cast<int>(ax::mojom::Dropeffect::kMinValue);
|
|
dropeffect_idx <= static_cast<int>(ax::mojom::Dropeffect::kMaxValue);
|
|
++dropeffect_idx) {
|
|
ax::mojom::Dropeffect dropeffect_enum =
|
|
static_cast<ax::mojom::Dropeffect>(dropeffect_idx);
|
|
if (HasDropeffect(dropeffect_enum))
|
|
str += " " + std::string(ui::ToString(dropeffect_enum));
|
|
}
|
|
|
|
// Removing leading space in final string.
|
|
return str.substr(1);
|
|
}
|
|
|
|
} // namespace ui
|