| // 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 "ax_node_data.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <iterator> | |
| #include <set> | |
| #include <utility> | |
| #include "ax_enum_util.h" | |
| #include "ax_role_properties.h" | |
| #include "base/container_utils.h" | |
| #include "base/logging.h" | |
| #include "base/no_destructor.h" | |
| #include "base/string_utils.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; | |
| } | |
| BASE_UNREACHABLE(); | |
| 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; | |
| } | |
| BASE_UNREACHABLE(); | |
| 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 = static_cast<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; | |
| } | |
| std::u16string AXNodeData::GetString16Attribute( | |
| ax::mojom::StringAttribute attribute) const { | |
| std::string value_utf8; | |
| if (!GetStringAttribute(attribute, &value_utf8)) | |
| return std::u16string(); | |
| return base::UTF8ToUTF16(value_utf8); | |
| } | |
| bool AXNodeData::GetString16Attribute(ax::mojom::StringAttribute attribute, | |
| std::u16string* 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, | |
| std::u16string* 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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(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) { | |
| if (role == ax::mojom::Role::kNone) { | |
| BASE_LOG() | |
| << "A valid role is required before setting the name attribute, " | |
| "because " | |
| "the role is used for setting the required NameFrom attribute."; | |
| BASE_UNREACHABLE(); | |
| } | |
| 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 std::u16string& 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 std::u16string& description) { | |
| SetDescription(base::UTF16ToUTF8(description)); | |
| } | |
| void AXNodeData::SetValue(const std::string& value) { | |
| AddStringAttribute(ax::mojom::StringAttribute::kValue, value); | |
| } | |
| void AXNodeData::SetValue(const std::u16string& value) { | |
| SetValue(base::UTF16ToUTF8(value)); | |
| } | |
| void AXNodeData::SetTooltip(const std::string& value) { | |
| AddStringAttribute(ax::mojom::StringAttribute::kTooltip, value); | |
| } | |
| void AXNodeData::SetTooltip(const std::u16string& value) { | |
| SetTooltip(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) { | |
| BASE_DCHECK(static_cast<int>(state_enum) > | |
| static_cast<int>(ax::mojom::State::kNone)); | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(static_cast<int>(state_enum) > | |
| static_cast<int>(ax::mojom::State::kNone)); | |
| BASE_DCHECK(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: | |
| BASE_UNREACHABLE(); | |
| 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; | |
| BASE_DCHECK(!HasAction(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) { | |
| BASE_DCHECK(static_cast<int>(text_style_enum) >= | |
| static_cast<int>(ax::mojom::TextStyle::kMinValue)); | |
| BASE_DCHECK(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) { | |
| BASE_DCHECK(static_cast<int>(dropeffect_enum) >= | |
| static_cast<int>(ax::mojom::Dropeffect::kMinValue)); | |
| BASE_DCHECK(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::IsInvisible() const { | |
| return HasState(ax::mojom::State::kInvisible); | |
| } | |
| bool AXNodeData::IsInvisibleOrIgnored() const { | |
| return IsIgnored() || IsInvisible(); | |
| } | |
| 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 |