Commit 45fe0314 authored by Alexander Surkov's avatar Alexander Surkov Committed by Commit Bot

a11y insepct reorg: move PropertyNode class to ui/accessibility

Bug: 1133330
Change-Id: I9103d0116c5822a0f9dc291a7a87bd5f902a4efa
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2519755
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825789}
parent 07513af4
......@@ -8,7 +8,6 @@
#include <memory>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/strings/pattern.h"
......@@ -21,6 +20,9 @@
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.h"
#include "ui/accessibility/platform/inspect/property_node.h"
using ui::AXPropertyNode;
namespace content {
......@@ -33,256 +35,6 @@ const char kSkipChildren[] = "@NO_CHILDREN_DUMP";
} // namespace
//
// PropertyNode
//
// static
PropertyNode PropertyNode::FromPropertyFilter(
const ui::AXPropertyFilter& filter) {
// Property invocation: property_str expected format is
// prop_name or prop_name(arg1, ... argN).
PropertyNode root;
const std::string& property_str = filter.property_str;
Parse(&root, property_str.begin(), property_str.end());
PropertyNode* node = &root.parameters[0];
// Expel a trailing wildcard if any.
node->original_property =
property_str.substr(0, property_str.find_last_of('*'));
// Line indexes filter: filter_str expected format is
// :line_num_1, ... :line_num_N, a comma separated list of line indexes
// the property should be queried for. For example, ":1,:5,:7" indicates that
// the property should called for objects placed on 1, 5 and 7 lines only.
const std::string& filter_str = filter.filter_str;
if (!filter_str.empty()) {
node->line_indexes =
base::SplitString(filter_str, std::string(1, ','),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
return std::move(*node);
}
PropertyNode::PropertyNode() = default;
PropertyNode::PropertyNode(PropertyNode&& o)
: key(std::move(o.key)),
target(std::move(o.target)),
name_or_value(std::move(o.name_or_value)),
parameters(std::move(o.parameters)),
original_property(std::move(o.original_property)),
line_indexes(std::move(o.line_indexes)) {}
PropertyNode::~PropertyNode() = default;
PropertyNode& PropertyNode::operator=(PropertyNode&& o) {
key = std::move(o.key);
target = std::move(o.target);
name_or_value = std::move(o.name_or_value);
parameters = std::move(o.parameters);
original_property = std::move(o.original_property);
line_indexes = std::move(o.line_indexes);
return *this;
}
PropertyNode::operator bool() const {
return !name_or_value.empty();
}
bool PropertyNode::IsMatching(const std::string& pattern) const {
// Looking for exact property match. Expel a trailing whildcard from
// the property filter to handle filters like AXRole*.
return name_or_value.compare(0, name_or_value.find_last_of('*'), pattern) ==
0;
}
bool PropertyNode::IsArray() const {
return name_or_value == "[]";
}
bool PropertyNode::IsDict() const {
return name_or_value == "{}";
}
base::Optional<int> PropertyNode::AsInt() const {
int value = 0;
if (!base::StringToInt(name_or_value, &value)) {
return base::nullopt;
}
return value;
}
const PropertyNode* PropertyNode::FindKey(const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return &param;
}
}
return nullptr;
}
base::Optional<std::string> PropertyNode::FindStringKey(
const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return param.name_or_value;
}
}
return base::nullopt;
}
base::Optional<int> PropertyNode::FindIntKey(const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return param.AsInt();
}
}
return base::nullopt;
}
std::string PropertyNode::ToString() const {
std::string out;
for (const auto& index : line_indexes) {
if (!out.empty()) {
out += ',';
}
out += index;
}
if (!out.empty()) {
out += ';';
}
if (!key.empty()) {
out += key + ": ";
}
if (!target.empty()) {
out += target + '.';
}
out += name_or_value;
if (parameters.size()) {
out += '(';
for (size_t i = 0; i < parameters.size(); i++) {
if (i != 0) {
out += ", ";
}
out += parameters[i].ToString();
}
out += ')';
}
return out;
}
// private
PropertyNode::PropertyNode(PropertyNode::iterator key_begin,
PropertyNode::iterator key_end,
const std::string& name_or_value)
: key(key_begin, key_end) {
Set(name_or_value.begin(), name_or_value.end());
}
PropertyNode::PropertyNode(PropertyNode::iterator begin,
PropertyNode::iterator end) {
Set(begin, end);
}
PropertyNode::PropertyNode(PropertyNode::iterator key_begin,
PropertyNode::iterator key_end,
PropertyNode::iterator value_begin,
PropertyNode::iterator value_end)
: key(key_begin, key_end), name_or_value(value_begin, value_end) {
Set(value_begin, value_end);
}
void PropertyNode::Set(PropertyNode::iterator begin,
PropertyNode::iterator end) {
PropertyNode::iterator dot_operator = std::find(begin, end, '.');
if (dot_operator != end) {
target = std::string(begin, dot_operator);
name_or_value = std::string(dot_operator + 1, end);
} else {
name_or_value = std::string(begin, end);
}
}
// private static
PropertyNode::iterator PropertyNode::Parse(PropertyNode* node,
PropertyNode::iterator begin,
PropertyNode::iterator end) {
auto iter = begin;
auto key_begin = end, key_end = end;
while (iter != end) {
// Subnode begins: create a new node, record its name and parse its
// arguments.
if (*iter == '(') {
node->parameters.push_back(PropertyNode(key_begin, key_end, begin, iter));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode begins: a special case for arrays, which have [arg1, ..., argN]
// form.
if (*iter == '[') {
node->parameters.push_back(PropertyNode(key_begin, key_end, "[]"));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode begins: a special case for dictionaries of {key1: value1, ...,
// key2: value2} form.
if (*iter == '{') {
node->parameters.push_back(PropertyNode(key_begin, key_end, "{}"));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode ends.
if (*iter == ')' || *iter == ']' || *iter == '}') {
if (begin != iter) {
node->parameters.push_back(
PropertyNode(key_begin, key_end, begin, iter));
key_begin = key_end = end;
}
return ++iter;
}
// Dictionary key
auto maybe_key_end = end;
if (*iter == ':') {
maybe_key_end = iter++;
}
// Skip spaces, adjust new node start.
if (*iter == ' ') {
if (maybe_key_end != end) {
key_begin = begin;
key_end = maybe_key_end;
}
begin = ++iter;
continue;
}
// Subsequent scalar param case.
if (*iter == ',' && begin != iter) {
node->parameters.push_back(PropertyNode(key_begin, key_end, begin, iter));
iter++;
key_begin = key_end = end;
begin = iter;
continue;
}
iter++;
}
// Single scalar param case.
if (begin != iter) {
node->parameters.push_back(PropertyNode(begin, iter));
}
return iter;
}
// static
std::string AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager(
BrowserAccessibilityManager* ax_mgr,
......@@ -387,12 +139,12 @@ void AccessibilityTreeFormatterBase::set_show_ids(bool show_ids) {
show_ids_ = show_ids;
}
std::vector<PropertyNode>
std::vector<AXPropertyNode>
AccessibilityTreeFormatterBase::PropertyFilterNodesFor(
const std::string& line_index) const {
std::vector<PropertyNode> list;
std::vector<AXPropertyNode> list;
for (const auto& filter : property_filters_) {
PropertyNode property_node = PropertyNode::FromPropertyFilter(filter);
AXPropertyNode property_node = AXPropertyNode::From(filter);
// Filter out if doesn't match line index (if specified).
if (!property_node.line_indexes.empty() &&
......
......@@ -25,81 +25,11 @@ namespace {
const char kChildrenDictAttr[] = "children";
}
namespace content {
// Property node is a tree-like structure, representing a property or collection
// of properties and its invocation parameters. A collection of properties is
// specified by putting a wildcard into a property name, for exampe, AXRole*
// will match both AXRole and AXRoleDescription properties. Parameters of a
// property are given in parentheses like a conventional function call, for
// example, AXCellForColumnAndRow([0, 0]) will call AXCellForColumnAndRow
// parameterized property for column/row 0 indexes.
class CONTENT_EXPORT PropertyNode final {
public:
// Parses a property node from a string.
static PropertyNode FromPropertyFilter(const ui::AXPropertyFilter& filter);
PropertyNode();
PropertyNode(PropertyNode&&);
~PropertyNode();
PropertyNode& operator=(PropertyNode&& other);
explicit operator bool() const;
// Key name in case of { key: value } dictionary.
std::string key;
// An object the property should be called for, designated by a line number
// in accessible tree the object is located at. For example, :1 indicates
// that the property should be called for an object located at first line.
std::string target;
// Value or a property name, for example 3 or AXLineForIndex
std::string name_or_value;
// Parameters if it's a property, for example, it is a vector of a single
// value 3 in case of AXLineForIndex(3)
std::vector<PropertyNode> parameters;
// Used to store the origianl unparsed property including invocation
// parameters if any.
std::string original_property;
// The list of line indexes of accessible objects the property is allowed to
// be called for, used if no property target is provided.
std::vector<std::string> line_indexes;
bool IsMatching(const std::string& pattern) const;
// Argument conversion methods.
bool IsArray() const;
bool IsDict() const;
base::Optional<int> AsInt() const;
const PropertyNode* FindKey(const char* refkey) const;
base::Optional<std::string> FindStringKey(const char* refkey) const;
base::Optional<int> FindIntKey(const char* key) const;
std::string ToString() const;
namespace ui {
class AXPropertyNode;
}
private:
using iterator = std::string::const_iterator;
explicit PropertyNode(iterator key_begin,
iterator key_end,
const std::string&);
PropertyNode(iterator begin, iterator end);
PropertyNode(iterator key_begin,
iterator key_end,
iterator value_begin,
iterator value_end);
// Helper to set context and name.
void Set(iterator begin, iterator end);
// Builds a property node struct for a string of NAME(ARG1, ..., ARGN) format,
// where each ARG is a scalar value or a string of the same format.
static iterator Parse(PropertyNode* node, iterator begin, iterator end);
};
namespace content {
// A utility class for formatting platform-specific accessibility information,
// for use in testing, debugging, and developer tools.
......@@ -161,7 +91,7 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBase
// Returns property nodes complying to the line index filter for all
// allow/allow_empty property filters.
std::vector<PropertyNode> PropertyFilterNodesFor(
std::vector<ui::AXPropertyNode> PropertyFilterNodesFor(
const std::string& line_index) const;
// Return true if match-all filter is present.
......
......@@ -15,6 +15,7 @@
#include "content/browser/accessibility/accessibility_tree_formatter_utils_mac.h"
#include "content/browser/accessibility/browser_accessibility_mac.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "ui/accessibility/platform/inspect/property_node.h"
// This file uses the deprecated NSObject accessibility interface.
// TODO(crbug.com/948844): Migrate to the new NSAccessibility interface.
......@@ -33,6 +34,7 @@ using content::a11y::IsBrowserAccessibilityCocoa;
using content::a11y::LineIndexer;
using content::a11y::OptionalNSObject;
using std::string;
using ui::AXPropertyNode;
namespace content {
......@@ -84,7 +86,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
// Invokes an attributes by a property node.
OptionalNSObject InvokeAttributeFor(
const BrowserAccessibilityCocoa* cocoa_node,
const PropertyNode& property_node,
const AXPropertyNode& property_node,
const LineIndexer* line_indexer) const;
base::Value PopulateSize(const BrowserAccessibilityCocoa*) const;
......@@ -228,7 +230,8 @@ void AccessibilityTreeFormatterMac::AddProperties(
// Otherwise dump attributes matching allow filters only.
std::string line_index = line_indexer->IndexBy(node);
for (const PropertyNode& property_node : PropertyFilterNodesFor(line_index)) {
for (const AXPropertyNode& property_node :
PropertyFilterNodesFor(line_index)) {
AttributeInvoker invoker(node, line_indexer);
OptionalNSObject value = invoker.Invoke(property_node);
if (value.IsNotApplicable()) {
......
......@@ -8,6 +8,10 @@
#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
#include "content/browser/accessibility/browser_accessibility_cocoa.h"
namespace ui {
class AXPropertyNode;
}
namespace content {
namespace a11y {
......@@ -69,7 +73,7 @@ class CONTENT_EXPORT AttributeInvoker final {
AttributeInvoker(const id node, const LineIndexer* line_indexer);
// Invokes an attribute matching to a property filter.
OptionalNSObject Invoke(const PropertyNode& property_node) const;
OptionalNSObject Invoke(const ui::AXPropertyNode& property_node) const;
// Gets the value of a parameterized attribute by name.
OptionalNSObject GetValue(const std::string& property_name,
const OptionalNSObject& param) const;
......@@ -81,16 +85,17 @@ class CONTENT_EXPORT AttributeInvoker final {
private:
// Returns a parameterized attribute parameter by a property node.
OptionalNSObject ParamByPropertyNode(const PropertyNode&) const;
OptionalNSObject ParamByPropertyNode(const ui::AXPropertyNode&) const;
NSNumber* PropertyNodeToInt(const PropertyNode&) const;
NSArray* PropertyNodeToIntArray(const PropertyNode&) const;
NSValue* PropertyNodeToRange(const PropertyNode&) const;
gfx::NativeViewAccessible PropertyNodeToUIElement(const PropertyNode&) const;
NSNumber* PropertyNodeToInt(const ui::AXPropertyNode&) const;
NSArray* PropertyNodeToIntArray(const ui::AXPropertyNode&) const;
NSValue* PropertyNodeToRange(const ui::AXPropertyNode&) const;
gfx::NativeViewAccessible PropertyNodeToUIElement(
const ui::AXPropertyNode&) const;
id DictNodeToTextMarker(const PropertyNode&) const;
id PropertyNodeToTextMarker(const PropertyNode&) const;
id PropertyNodeToTextMarkerRange(const PropertyNode&) const;
id DictNodeToTextMarker(const ui::AXPropertyNode&) const;
id PropertyNodeToTextMarker(const ui::AXPropertyNode&) const;
id PropertyNodeToTextMarkerRange(const ui::AXPropertyNode&) const;
gfx::NativeViewAccessible LineIndexToNode(
const base::string16 line_index) const;
......
......@@ -7,6 +7,9 @@
#include "base/strings/sys_string_conversions.h"
#include "content/browser/accessibility/accessibility_tools_utils_mac.h"
#include "content/browser/accessibility/browser_accessibility_mac.h"
#include "ui/accessibility/platform/inspect/property_node.h"
using ui::AXPropertyNode;
namespace content {
namespace a11y {
......@@ -114,7 +117,7 @@ AttributeInvoker::AttributeInvoker(const id node,
}
OptionalNSObject AttributeInvoker::Invoke(
const PropertyNode& property_node) const {
const AXPropertyNode& property_node) const {
// Attributes
for (NSString* attribute : attributes) {
if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
......@@ -173,7 +176,7 @@ void AttributeInvoker::SetValue(const std::string& property_name,
}
OptionalNSObject AttributeInvoker::ParamByPropertyNode(
const PropertyNode& property_node) const {
const AXPropertyNode& property_node) const {
// NSAccessibility attributes always take a single parameter.
if (property_node.parameters.size() != 1) {
LOG(ERROR) << "Failed to parse " << property_node.original_property
......@@ -182,7 +185,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode(
}
// Nested attribute case: attempt to invoke an attribute for an argument node.
const PropertyNode& arg_node = property_node.parameters[0];
const AXPropertyNode& arg_node = property_node.parameters[0];
OptionalNSObject subvalue = Invoke(arg_node);
if (!subvalue.IsNotApplicable()) {
return subvalue;
......@@ -216,7 +219,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode(
// NSNumber. Format: integer.
NSNumber* AttributeInvoker::PropertyNodeToInt(
const PropertyNode& intnode) const {
const AXPropertyNode& intnode) const {
base::Optional<int> param = intnode.AsInt();
if (!param) {
INT_FAIL(intnode, "not a number")
......@@ -226,7 +229,7 @@ NSNumber* AttributeInvoker::PropertyNodeToInt(
// NSArray of two NSNumber. Format: [integer, integer].
NSArray* AttributeInvoker::PropertyNodeToIntArray(
const PropertyNode& arraynode) const {
const AXPropertyNode& arraynode) const {
if (arraynode.name_or_value != "[]") {
INTARRAY_FAIL(arraynode, "not array")
}
......@@ -245,7 +248,7 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray(
// NSRange. Format: {loc: integer, len: integer}.
NSValue* AttributeInvoker::PropertyNodeToRange(
const PropertyNode& dictnode) const {
const AXPropertyNode& dictnode) const {
if (!dictnode.IsDict()) {
NSRANGE_FAIL(dictnode, "dictionary is expected")
}
......@@ -265,7 +268,7 @@ NSValue* AttributeInvoker::PropertyNodeToRange(
// UIElement. Format: :line_num.
gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement(
const PropertyNode& uielement_node) const {
const AXPropertyNode& uielement_node) const {
gfx::NativeViewAccessible uielement =
line_indexer->NodeBy(uielement_node.name_or_value);
if (!uielement) {
......@@ -275,7 +278,8 @@ gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement(
return uielement;
}
id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const {
id AttributeInvoker::DictNodeToTextMarker(
const AXPropertyNode& dictnode) const {
if (!dictnode.IsDict()) {
TEXTMARKER_FAIL(dictnode, "dictionary is expected")
}
......@@ -310,17 +314,17 @@ id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const {
}
id AttributeInvoker::PropertyNodeToTextMarker(
const PropertyNode& dictnode) const {
const AXPropertyNode& dictnode) const {
return DictNodeToTextMarker(dictnode);
}
id AttributeInvoker::PropertyNodeToTextMarkerRange(
const PropertyNode& rangenode) const {
const AXPropertyNode& rangenode) const {
if (!rangenode.IsDict()) {
TEXTMARKER_FAIL(rangenode, "dictionary is expected")
}
const PropertyNode* anchornode = rangenode.FindKey("anchor");
const AXPropertyNode* anchornode = rangenode.FindKey("anchor");
if (!anchornode) {
TEXTMARKER_FAIL(rangenode, "no anchor")
}
......@@ -330,7 +334,7 @@ id AttributeInvoker::PropertyNodeToTextMarkerRange(
TEXTMARKER_FAIL(rangenode, "failed to parse anchor")
}
const PropertyNode* focusnode = rangenode.FindKey("focus");
const AXPropertyNode* focusnode = rangenode.FindKey("focus");
if (!focusnode) {
TEXTMARKER_FAIL(rangenode, "no focus")
}
......
......@@ -1705,7 +1705,6 @@ test("content_unittests") {
defines = []
sources = [
"../browser/accessibility/accessibility_tree_formatter_base_unittest.cc",
"../browser/accessibility/browser_accessibility_manager_unittest.cc",
"../browser/accessibility/browser_accessibility_unittest.cc",
"../browser/accessibility/one_shot_accessibility_tree_search_unittest.cc",
......
......@@ -238,6 +238,7 @@ test("accessibility_unittests") {
"platform/ax_platform_node_unittest.cc",
"platform/ax_platform_node_unittest.h",
"platform/ax_unique_id_unittest.cc",
"platform/inspect/property_node_unittest.cc",
"run_all_unittests.cc",
]
......
......@@ -61,6 +61,8 @@ source_set("platform") {
# Used by DumpAccTree testsuite and a11y tools
"inspect/inspect.cc",
"inspect/inspect.h",
"inspect/property_node.cc",
"inspect/property_node.h",
"inspect/tree_formatter.cc",
"inspect/tree_formatter.h",
]
......
// Copyright 2019 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/platform/inspect/property_node.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "ui/accessibility/platform/inspect/inspect.h"
namespace ui {
// static
AXPropertyNode AXPropertyNode::From(const AXPropertyFilter& filter) {
// Property invocation: property_str expected format is
// prop_name or prop_name(arg1, ... argN).
AXPropertyNode root;
const std::string& property_str = filter.property_str;
Parse(&root, property_str.begin(), property_str.end());
AXPropertyNode* node = &root.parameters[0];
// Expel a trailing wildcard if any.
node->original_property =
property_str.substr(0, property_str.find_last_of('*'));
// Line indexes filter: filter_str expected format is
// :line_num_1, ... :line_num_N, a comma separated list of line indexes
// the property should be queried for. For example, ":1,:5,:7" indicates that
// the property should called for objects placed on 1, 5 and 7 lines only.
const std::string& filter_str = filter.filter_str;
if (!filter_str.empty()) {
node->line_indexes =
base::SplitString(filter_str, std::string(1, ','),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
return std::move(*node);
}
AXPropertyNode::AXPropertyNode() = default;
AXPropertyNode::AXPropertyNode(AXPropertyNode&& o)
: key(std::move(o.key)),
target(std::move(o.target)),
name_or_value(std::move(o.name_or_value)),
parameters(std::move(o.parameters)),
original_property(std::move(o.original_property)),
line_indexes(std::move(o.line_indexes)) {}
AXPropertyNode::~AXPropertyNode() = default;
AXPropertyNode& AXPropertyNode::operator=(AXPropertyNode&& o) {
key = std::move(o.key);
target = std::move(o.target);
name_or_value = std::move(o.name_or_value);
parameters = std::move(o.parameters);
original_property = std::move(o.original_property);
line_indexes = std::move(o.line_indexes);
return *this;
}
AXPropertyNode::operator bool() const {
return !name_or_value.empty();
}
bool AXPropertyNode::IsMatching(const std::string& pattern) const {
// Looking for exact property match. Expel a trailing whildcard from
// the property filter to handle filters like AXRole*.
return name_or_value.compare(0, name_or_value.find_last_of('*'), pattern) ==
0;
}
bool AXPropertyNode::IsArray() const {
return name_or_value == "[]";
}
bool AXPropertyNode::IsDict() const {
return name_or_value == "{}";
}
base::Optional<int> AXPropertyNode::AsInt() const {
int value = 0;
if (!base::StringToInt(name_or_value, &value)) {
return base::nullopt;
}
return value;
}
const AXPropertyNode* AXPropertyNode::FindKey(const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return &param;
}
}
return nullptr;
}
base::Optional<std::string> AXPropertyNode::FindStringKey(
const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return param.name_or_value;
}
}
return base::nullopt;
}
base::Optional<int> AXPropertyNode::FindIntKey(const char* refkey) const {
for (const auto& param : parameters) {
if (param.key == refkey) {
return param.AsInt();
}
}
return base::nullopt;
}
std::string AXPropertyNode::ToString() const {
std::string out;
for (const auto& index : line_indexes) {
if (!out.empty()) {
out += ',';
}
out += index;
}
if (!out.empty()) {
out += ';';
}
if (!key.empty()) {
out += key + ": ";
}
if (!target.empty()) {
out += target + '.';
}
out += name_or_value;
if (parameters.size()) {
out += '(';
for (size_t i = 0; i < parameters.size(); i++) {
if (i != 0) {
out += ", ";
}
out += parameters[i].ToString();
}
out += ')';
}
return out;
}
// private
AXPropertyNode::AXPropertyNode(AXPropertyNode::iterator key_begin,
AXPropertyNode::iterator key_end,
const std::string& name_or_value)
: key(key_begin, key_end) {
Set(name_or_value.begin(), name_or_value.end());
}
AXPropertyNode::AXPropertyNode(AXPropertyNode::iterator begin,
AXPropertyNode::iterator end) {
Set(begin, end);
}
AXPropertyNode::AXPropertyNode(AXPropertyNode::iterator key_begin,
AXPropertyNode::iterator key_end,
AXPropertyNode::iterator value_begin,
AXPropertyNode::iterator value_end)
: key(key_begin, key_end), name_or_value(value_begin, value_end) {
Set(value_begin, value_end);
}
void AXPropertyNode::Set(AXPropertyNode::iterator begin,
AXPropertyNode::iterator end) {
AXPropertyNode::iterator dot_operator = std::find(begin, end, '.');
if (dot_operator != end) {
target = std::string(begin, dot_operator);
name_or_value = std::string(dot_operator + 1, end);
} else {
name_or_value = std::string(begin, end);
}
}
// private static
AXPropertyNode::iterator AXPropertyNode::Parse(AXPropertyNode* node,
AXPropertyNode::iterator begin,
AXPropertyNode::iterator end) {
auto iter = begin;
auto key_begin = end, key_end = end;
while (iter != end) {
// Subnode begins: create a new node, record its name and parse its
// arguments.
if (*iter == '(') {
node->parameters.push_back(
AXPropertyNode(key_begin, key_end, begin, iter));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode begins: a special case for arrays, which have [arg1, ..., argN]
// form.
if (*iter == '[') {
node->parameters.push_back(AXPropertyNode(key_begin, key_end, "[]"));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode begins: a special case for dictionaries of {key1: value1, ...,
// key2: value2} form.
if (*iter == '{') {
node->parameters.push_back(AXPropertyNode(key_begin, key_end, "{}"));
key_begin = key_end = end;
begin = iter = Parse(&node->parameters.back(), ++iter, end);
continue;
}
// Subnode ends.
if (*iter == ')' || *iter == ']' || *iter == '}') {
if (begin != iter) {
node->parameters.push_back(
AXPropertyNode(key_begin, key_end, begin, iter));
key_begin = key_end = end;
}
return ++iter;
}
// Dictionary key
auto maybe_key_end = end;
if (*iter == ':') {
maybe_key_end = iter++;
}
// Skip spaces, adjust new node start.
if (*iter == ' ') {
if (maybe_key_end != end) {
key_begin = begin;
key_end = maybe_key_end;
}
begin = ++iter;
continue;
}
// Subsequent scalar param case.
if (*iter == ',' && begin != iter) {
node->parameters.push_back(
AXPropertyNode(key_begin, key_end, begin, iter));
iter++;
key_begin = key_end = end;
begin = iter;
continue;
}
iter++;
}
// Single scalar param case.
if (begin != iter) {
node->parameters.push_back(AXPropertyNode(begin, iter));
}
return iter;
}
} // namespace ui
// Copyright 2020 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.
#ifndef UI_ACCESSIBILITY_PLATFORM_INSPECT_PROPERTY_NODE_H_
#define UI_ACCESSIBILITY_PLATFORM_INSPECT_PROPERTY_NODE_H_
#include <string>
#include <vector>
#include "ui/accessibility/ax_export.h"
namespace base {
template <typename T>
class Optional;
}
namespace ui {
struct AXPropertyFilter;
// Property node is a tree-like structure, representing a property or collection
// of properties and its invocation parameters. A collection of properties is
// specified by putting a wildcard into a property name, for exampe, AXRole*
// will match both AXRole and AXRoleDescription properties. Parameters of a
// property are given in parentheses like a conventional function call, for
// example, AXCellForColumnAndRow([0, 0]) will call AXCellForColumnAndRow
// parameterized property for column/row 0 indexes.
class AX_EXPORT AXPropertyNode final {
public:
// Parses a property node from a property filter.
static AXPropertyNode From(const AXPropertyFilter& filter);
AXPropertyNode();
AXPropertyNode(AXPropertyNode&&);
~AXPropertyNode();
AXPropertyNode& operator=(AXPropertyNode&& other);
explicit operator bool() const;
// Key name in case of { key: value } dictionary.
std::string key;
// An object the property should be called for, designated by a line number
// in accessible tree the object is located at. For example, :1 indicates
// that the property should be called for an object located at first line.
std::string target;
// Value or a property name, for example 3 or AXLineForIndex
std::string name_or_value;
// Parameters if it's a property, for example, it is a vector of a single
// value 3 in case of AXLineForIndex(3)
std::vector<AXPropertyNode> parameters;
// Used to store the origianl unparsed property including invocation
// parameters if any.
std::string original_property;
// The list of line indexes of accessible objects the property is allowed to
// be called for, used if no property target is provided.
std::vector<std::string> line_indexes;
bool IsMatching(const std::string& pattern) const;
// Argument conversion methods.
bool IsArray() const;
bool IsDict() const;
base::Optional<int> AsInt() const;
const AXPropertyNode* FindKey(const char* refkey) const;
base::Optional<std::string> FindStringKey(const char* refkey) const;
base::Optional<int> FindIntKey(const char* key) const;
std::string ToString() const;
private:
using iterator = std::string::const_iterator;
explicit AXPropertyNode(iterator key_begin,
iterator key_end,
const std::string&);
AXPropertyNode(iterator begin, iterator end);
AXPropertyNode(iterator key_begin,
iterator key_end,
iterator value_begin,
iterator value_end);
// Helper to set context and name.
void Set(iterator begin, iterator end);
// Builds a property node struct for a string of NAME(ARG1, ..., ARGN) format,
// where each ARG is a scalar value or a string of the same format.
static iterator Parse(AXPropertyNode* node, iterator begin, iterator end);
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_INSPECT_PROPERTY_NODE_H_
......@@ -2,43 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
#include "content/browser/accessibility/browser_accessibility.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/test_browser_accessibility_delegate.h"
#include "ui/accessibility/platform/inspect/property_node.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/platform/inspect/inspect.h"
namespace content {
using ui::AXPropertyFilter;
using ui::AXPropertyNode;
class AccessibilityTreeFormatterBaseTest : public testing::Test {
public:
AccessibilityTreeFormatterBaseTest() = default;
~AccessibilityTreeFormatterBaseTest() override = default;
namespace ui {
protected:
std::unique_ptr<TestBrowserAccessibilityDelegate>
test_browser_accessibility_delegate_;
class AXPropertyNodeTest : public testing::Test {
public:
AXPropertyNodeTest() = default;
~AXPropertyNodeTest() override = default;
private:
void SetUp() override {
test_browser_accessibility_delegate_ =
std::make_unique<TestBrowserAccessibilityDelegate>();
}
DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatterBaseTest);
DISALLOW_COPY_AND_ASSIGN(AXPropertyNodeTest);
};
PropertyNode Parse(const char* input) {
ui::AXPropertyFilter filter(input, ui::AXPropertyFilter::ALLOW);
return PropertyNode::FromPropertyFilter(filter);
AXPropertyNode Parse(const char* input) {
AXPropertyFilter filter(input, AXPropertyFilter::ALLOW);
return AXPropertyNode::From(filter);
}
PropertyNode GetArgumentNode(const char* input) {
AXPropertyNode GetArgumentNode(const char* input) {
auto got = Parse(input);
if (got.parameters.size() == 0) {
return PropertyNode();
return AXPropertyNode();
}
return std::move(got.parameters[0]);
}
......@@ -54,7 +45,7 @@ struct ProperyNodeCheck {
std::vector<ProperyNodeCheck> parameters;
};
void Check(const PropertyNode& got, const ProperyNodeCheck& expected) {
void Check(const AXPropertyNode& got, const ProperyNodeCheck& expected) {
EXPECT_EQ(got.target, expected.target);
EXPECT_EQ(got.name_or_value, expected.name_or_value);
EXPECT_EQ(got.parameters.size(), expected.parameters.size());
......@@ -68,7 +59,7 @@ void ParseAndCheck(const char* input, const ProperyNodeCheck& expected) {
Check(Parse(input), expected);
}
TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) {
TEST_F(AXPropertyNodeTest, ParseProperty) {
// Properties and methods.
ParseAndCheck("Role", "Role");
ParseAndCheck("ChildAt(3)", "ChildAt(3)");
......@@ -151,4 +142,4 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) {
"anchor: {}(:2, 1, down)");
}
} // namespace content
} // namespace ui
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment