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 @@ ...@@ -8,7 +8,6 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector>
#include "base/check_op.h" #include "base/check_op.h"
#include "base/strings/pattern.h" #include "base/strings/pattern.h"
...@@ -21,6 +20,9 @@ ...@@ -21,6 +20,9 @@
#include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "ui/accessibility/platform/inspect/property_node.h"
using ui::AXPropertyNode;
namespace content { namespace content {
...@@ -33,256 +35,6 @@ const char kSkipChildren[] = "@NO_CHILDREN_DUMP"; ...@@ -33,256 +35,6 @@ const char kSkipChildren[] = "@NO_CHILDREN_DUMP";
} // namespace } // 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 // static
std::string AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager( std::string AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager(
BrowserAccessibilityManager* ax_mgr, BrowserAccessibilityManager* ax_mgr,
...@@ -387,12 +139,12 @@ void AccessibilityTreeFormatterBase::set_show_ids(bool show_ids) { ...@@ -387,12 +139,12 @@ void AccessibilityTreeFormatterBase::set_show_ids(bool show_ids) {
show_ids_ = show_ids; show_ids_ = show_ids;
} }
std::vector<PropertyNode> std::vector<AXPropertyNode>
AccessibilityTreeFormatterBase::PropertyFilterNodesFor( AccessibilityTreeFormatterBase::PropertyFilterNodesFor(
const std::string& line_index) const { const std::string& line_index) const {
std::vector<PropertyNode> list; std::vector<AXPropertyNode> list;
for (const auto& filter : property_filters_) { 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). // Filter out if doesn't match line index (if specified).
if (!property_node.line_indexes.empty() && if (!property_node.line_indexes.empty() &&
......
...@@ -25,81 +25,11 @@ namespace { ...@@ -25,81 +25,11 @@ namespace {
const char kChildrenDictAttr[] = "children"; const char kChildrenDictAttr[] = "children";
} }
namespace content { namespace ui {
class AXPropertyNode;
// 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;
private: namespace content {
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);
};
// A utility class for formatting platform-specific accessibility information, // A utility class for formatting platform-specific accessibility information,
// for use in testing, debugging, and developer tools. // for use in testing, debugging, and developer tools.
...@@ -161,7 +91,7 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBase ...@@ -161,7 +91,7 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBase
// Returns property nodes complying to the line index filter for all // Returns property nodes complying to the line index filter for all
// allow/allow_empty property filters. // allow/allow_empty property filters.
std::vector<PropertyNode> PropertyFilterNodesFor( std::vector<ui::AXPropertyNode> PropertyFilterNodesFor(
const std::string& line_index) const; const std::string& line_index) const;
// Return true if match-all filter is present. // Return true if match-all filter is present.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "content/browser/accessibility/accessibility_tree_formatter_utils_mac.h" #include "content/browser/accessibility/accessibility_tree_formatter_utils_mac.h"
#include "content/browser/accessibility/browser_accessibility_mac.h" #include "content/browser/accessibility/browser_accessibility_mac.h"
#include "content/browser/accessibility/browser_accessibility_manager.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. // This file uses the deprecated NSObject accessibility interface.
// TODO(crbug.com/948844): Migrate to the new NSAccessibility interface. // TODO(crbug.com/948844): Migrate to the new NSAccessibility interface.
...@@ -33,6 +34,7 @@ using content::a11y::IsBrowserAccessibilityCocoa; ...@@ -33,6 +34,7 @@ using content::a11y::IsBrowserAccessibilityCocoa;
using content::a11y::LineIndexer; using content::a11y::LineIndexer;
using content::a11y::OptionalNSObject; using content::a11y::OptionalNSObject;
using std::string; using std::string;
using ui::AXPropertyNode;
namespace content { namespace content {
...@@ -84,7 +86,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { ...@@ -84,7 +86,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
// Invokes an attributes by a property node. // Invokes an attributes by a property node.
OptionalNSObject InvokeAttributeFor( OptionalNSObject InvokeAttributeFor(
const BrowserAccessibilityCocoa* cocoa_node, const BrowserAccessibilityCocoa* cocoa_node,
const PropertyNode& property_node, const AXPropertyNode& property_node,
const LineIndexer* line_indexer) const; const LineIndexer* line_indexer) const;
base::Value PopulateSize(const BrowserAccessibilityCocoa*) const; base::Value PopulateSize(const BrowserAccessibilityCocoa*) const;
...@@ -228,7 +230,8 @@ void AccessibilityTreeFormatterMac::AddProperties( ...@@ -228,7 +230,8 @@ void AccessibilityTreeFormatterMac::AddProperties(
// Otherwise dump attributes matching allow filters only. // Otherwise dump attributes matching allow filters only.
std::string line_index = line_indexer->IndexBy(node); 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); AttributeInvoker invoker(node, line_indexer);
OptionalNSObject value = invoker.Invoke(property_node); OptionalNSObject value = invoker.Invoke(property_node);
if (value.IsNotApplicable()) { if (value.IsNotApplicable()) {
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#include "content/browser/accessibility/accessibility_tree_formatter_base.h" #include "content/browser/accessibility/accessibility_tree_formatter_base.h"
#include "content/browser/accessibility/browser_accessibility_cocoa.h" #include "content/browser/accessibility/browser_accessibility_cocoa.h"
namespace ui {
class AXPropertyNode;
}
namespace content { namespace content {
namespace a11y { namespace a11y {
...@@ -69,7 +73,7 @@ class CONTENT_EXPORT AttributeInvoker final { ...@@ -69,7 +73,7 @@ class CONTENT_EXPORT AttributeInvoker final {
AttributeInvoker(const id node, const LineIndexer* line_indexer); AttributeInvoker(const id node, const LineIndexer* line_indexer);
// Invokes an attribute matching to a property filter. // 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. // Gets the value of a parameterized attribute by name.
OptionalNSObject GetValue(const std::string& property_name, OptionalNSObject GetValue(const std::string& property_name,
const OptionalNSObject& param) const; const OptionalNSObject& param) const;
...@@ -81,16 +85,17 @@ class CONTENT_EXPORT AttributeInvoker final { ...@@ -81,16 +85,17 @@ class CONTENT_EXPORT AttributeInvoker final {
private: private:
// Returns a parameterized attribute parameter by a property node. // 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; NSNumber* PropertyNodeToInt(const ui::AXPropertyNode&) const;
NSArray* PropertyNodeToIntArray(const PropertyNode&) const; NSArray* PropertyNodeToIntArray(const ui::AXPropertyNode&) const;
NSValue* PropertyNodeToRange(const PropertyNode&) const; NSValue* PropertyNodeToRange(const ui::AXPropertyNode&) const;
gfx::NativeViewAccessible PropertyNodeToUIElement(const PropertyNode&) const; gfx::NativeViewAccessible PropertyNodeToUIElement(
const ui::AXPropertyNode&) const;
id DictNodeToTextMarker(const PropertyNode&) const; id DictNodeToTextMarker(const ui::AXPropertyNode&) const;
id PropertyNodeToTextMarker(const PropertyNode&) const; id PropertyNodeToTextMarker(const ui::AXPropertyNode&) const;
id PropertyNodeToTextMarkerRange(const PropertyNode&) const; id PropertyNodeToTextMarkerRange(const ui::AXPropertyNode&) const;
gfx::NativeViewAccessible LineIndexToNode( gfx::NativeViewAccessible LineIndexToNode(
const base::string16 line_index) const; const base::string16 line_index) const;
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "content/browser/accessibility/accessibility_tools_utils_mac.h" #include "content/browser/accessibility/accessibility_tools_utils_mac.h"
#include "content/browser/accessibility/browser_accessibility_mac.h" #include "content/browser/accessibility/browser_accessibility_mac.h"
#include "ui/accessibility/platform/inspect/property_node.h"
using ui::AXPropertyNode;
namespace content { namespace content {
namespace a11y { namespace a11y {
...@@ -114,7 +117,7 @@ AttributeInvoker::AttributeInvoker(const id node, ...@@ -114,7 +117,7 @@ AttributeInvoker::AttributeInvoker(const id node,
} }
OptionalNSObject AttributeInvoker::Invoke( OptionalNSObject AttributeInvoker::Invoke(
const PropertyNode& property_node) const { const AXPropertyNode& property_node) const {
// Attributes // Attributes
for (NSString* attribute : attributes) { for (NSString* attribute : attributes) {
if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) { if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
...@@ -173,7 +176,7 @@ void AttributeInvoker::SetValue(const std::string& property_name, ...@@ -173,7 +176,7 @@ void AttributeInvoker::SetValue(const std::string& property_name,
} }
OptionalNSObject AttributeInvoker::ParamByPropertyNode( OptionalNSObject AttributeInvoker::ParamByPropertyNode(
const PropertyNode& property_node) const { const AXPropertyNode& property_node) const {
// NSAccessibility attributes always take a single parameter. // NSAccessibility attributes always take a single parameter.
if (property_node.parameters.size() != 1) { if (property_node.parameters.size() != 1) {
LOG(ERROR) << "Failed to parse " << property_node.original_property LOG(ERROR) << "Failed to parse " << property_node.original_property
...@@ -182,7 +185,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode( ...@@ -182,7 +185,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode(
} }
// Nested attribute case: attempt to invoke an attribute for an argument node. // 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); OptionalNSObject subvalue = Invoke(arg_node);
if (!subvalue.IsNotApplicable()) { if (!subvalue.IsNotApplicable()) {
return subvalue; return subvalue;
...@@ -216,7 +219,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode( ...@@ -216,7 +219,7 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode(
// NSNumber. Format: integer. // NSNumber. Format: integer.
NSNumber* AttributeInvoker::PropertyNodeToInt( NSNumber* AttributeInvoker::PropertyNodeToInt(
const PropertyNode& intnode) const { const AXPropertyNode& intnode) const {
base::Optional<int> param = intnode.AsInt(); base::Optional<int> param = intnode.AsInt();
if (!param) { if (!param) {
INT_FAIL(intnode, "not a number") INT_FAIL(intnode, "not a number")
...@@ -226,7 +229,7 @@ NSNumber* AttributeInvoker::PropertyNodeToInt( ...@@ -226,7 +229,7 @@ NSNumber* AttributeInvoker::PropertyNodeToInt(
// NSArray of two NSNumber. Format: [integer, integer]. // NSArray of two NSNumber. Format: [integer, integer].
NSArray* AttributeInvoker::PropertyNodeToIntArray( NSArray* AttributeInvoker::PropertyNodeToIntArray(
const PropertyNode& arraynode) const { const AXPropertyNode& arraynode) const {
if (arraynode.name_or_value != "[]") { if (arraynode.name_or_value != "[]") {
INTARRAY_FAIL(arraynode, "not array") INTARRAY_FAIL(arraynode, "not array")
} }
...@@ -245,7 +248,7 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray( ...@@ -245,7 +248,7 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray(
// NSRange. Format: {loc: integer, len: integer}. // NSRange. Format: {loc: integer, len: integer}.
NSValue* AttributeInvoker::PropertyNodeToRange( NSValue* AttributeInvoker::PropertyNodeToRange(
const PropertyNode& dictnode) const { const AXPropertyNode& dictnode) const {
if (!dictnode.IsDict()) { if (!dictnode.IsDict()) {
NSRANGE_FAIL(dictnode, "dictionary is expected") NSRANGE_FAIL(dictnode, "dictionary is expected")
} }
...@@ -265,7 +268,7 @@ NSValue* AttributeInvoker::PropertyNodeToRange( ...@@ -265,7 +268,7 @@ NSValue* AttributeInvoker::PropertyNodeToRange(
// UIElement. Format: :line_num. // UIElement. Format: :line_num.
gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement( gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement(
const PropertyNode& uielement_node) const { const AXPropertyNode& uielement_node) const {
gfx::NativeViewAccessible uielement = gfx::NativeViewAccessible uielement =
line_indexer->NodeBy(uielement_node.name_or_value); line_indexer->NodeBy(uielement_node.name_or_value);
if (!uielement) { if (!uielement) {
...@@ -275,7 +278,8 @@ gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement( ...@@ -275,7 +278,8 @@ gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement(
return uielement; return uielement;
} }
id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const { id AttributeInvoker::DictNodeToTextMarker(
const AXPropertyNode& dictnode) const {
if (!dictnode.IsDict()) { if (!dictnode.IsDict()) {
TEXTMARKER_FAIL(dictnode, "dictionary is expected") TEXTMARKER_FAIL(dictnode, "dictionary is expected")
} }
...@@ -310,17 +314,17 @@ id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const { ...@@ -310,17 +314,17 @@ id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const {
} }
id AttributeInvoker::PropertyNodeToTextMarker( id AttributeInvoker::PropertyNodeToTextMarker(
const PropertyNode& dictnode) const { const AXPropertyNode& dictnode) const {
return DictNodeToTextMarker(dictnode); return DictNodeToTextMarker(dictnode);
} }
id AttributeInvoker::PropertyNodeToTextMarkerRange( id AttributeInvoker::PropertyNodeToTextMarkerRange(
const PropertyNode& rangenode) const { const AXPropertyNode& rangenode) const {
if (!rangenode.IsDict()) { if (!rangenode.IsDict()) {
TEXTMARKER_FAIL(rangenode, "dictionary is expected") TEXTMARKER_FAIL(rangenode, "dictionary is expected")
} }
const PropertyNode* anchornode = rangenode.FindKey("anchor"); const AXPropertyNode* anchornode = rangenode.FindKey("anchor");
if (!anchornode) { if (!anchornode) {
TEXTMARKER_FAIL(rangenode, "no anchor") TEXTMARKER_FAIL(rangenode, "no anchor")
} }
...@@ -330,7 +334,7 @@ id AttributeInvoker::PropertyNodeToTextMarkerRange( ...@@ -330,7 +334,7 @@ id AttributeInvoker::PropertyNodeToTextMarkerRange(
TEXTMARKER_FAIL(rangenode, "failed to parse anchor") TEXTMARKER_FAIL(rangenode, "failed to parse anchor")
} }
const PropertyNode* focusnode = rangenode.FindKey("focus"); const AXPropertyNode* focusnode = rangenode.FindKey("focus");
if (!focusnode) { if (!focusnode) {
TEXTMARKER_FAIL(rangenode, "no focus") TEXTMARKER_FAIL(rangenode, "no focus")
} }
......
...@@ -1705,7 +1705,6 @@ test("content_unittests") { ...@@ -1705,7 +1705,6 @@ test("content_unittests") {
defines = [] defines = []
sources = [ sources = [
"../browser/accessibility/accessibility_tree_formatter_base_unittest.cc",
"../browser/accessibility/browser_accessibility_manager_unittest.cc", "../browser/accessibility/browser_accessibility_manager_unittest.cc",
"../browser/accessibility/browser_accessibility_unittest.cc", "../browser/accessibility/browser_accessibility_unittest.cc",
"../browser/accessibility/one_shot_accessibility_tree_search_unittest.cc", "../browser/accessibility/one_shot_accessibility_tree_search_unittest.cc",
......
...@@ -238,6 +238,7 @@ test("accessibility_unittests") { ...@@ -238,6 +238,7 @@ test("accessibility_unittests") {
"platform/ax_platform_node_unittest.cc", "platform/ax_platform_node_unittest.cc",
"platform/ax_platform_node_unittest.h", "platform/ax_platform_node_unittest.h",
"platform/ax_unique_id_unittest.cc", "platform/ax_unique_id_unittest.cc",
"platform/inspect/property_node_unittest.cc",
"run_all_unittests.cc", "run_all_unittests.cc",
] ]
......
...@@ -61,6 +61,8 @@ source_set("platform") { ...@@ -61,6 +61,8 @@ source_set("platform") {
# Used by DumpAccTree testsuite and a11y tools # Used by DumpAccTree testsuite and a11y tools
"inspect/inspect.cc", "inspect/inspect.cc",
"inspect/inspect.h", "inspect/inspect.h",
"inspect/property_node.cc",
"inspect/property_node.h",
"inspect/tree_formatter.cc", "inspect/tree_formatter.cc",
"inspect/tree_formatter.h", "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 @@ ...@@ -2,43 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "content/browser/accessibility/accessibility_tree_formatter_base.h" #include "ui/accessibility/platform/inspect/property_node.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 "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.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 { namespace ui {
public:
AccessibilityTreeFormatterBaseTest() = default;
~AccessibilityTreeFormatterBaseTest() override = default;
protected: class AXPropertyNodeTest : public testing::Test {
std::unique_ptr<TestBrowserAccessibilityDelegate> public:
test_browser_accessibility_delegate_; AXPropertyNodeTest() = default;
~AXPropertyNodeTest() override = default;
private: private:
void SetUp() override { DISALLOW_COPY_AND_ASSIGN(AXPropertyNodeTest);
test_browser_accessibility_delegate_ =
std::make_unique<TestBrowserAccessibilityDelegate>();
}
DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatterBaseTest);
}; };
PropertyNode Parse(const char* input) { AXPropertyNode Parse(const char* input) {
ui::AXPropertyFilter filter(input, ui::AXPropertyFilter::ALLOW); AXPropertyFilter filter(input, AXPropertyFilter::ALLOW);
return PropertyNode::FromPropertyFilter(filter); return AXPropertyNode::From(filter);
} }
PropertyNode GetArgumentNode(const char* input) { AXPropertyNode GetArgumentNode(const char* input) {
auto got = Parse(input); auto got = Parse(input);
if (got.parameters.size() == 0) { if (got.parameters.size() == 0) {
return PropertyNode(); return AXPropertyNode();
} }
return std::move(got.parameters[0]); return std::move(got.parameters[0]);
} }
...@@ -54,7 +45,7 @@ struct ProperyNodeCheck { ...@@ -54,7 +45,7 @@ struct ProperyNodeCheck {
std::vector<ProperyNodeCheck> parameters; 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.target, expected.target);
EXPECT_EQ(got.name_or_value, expected.name_or_value); EXPECT_EQ(got.name_or_value, expected.name_or_value);
EXPECT_EQ(got.parameters.size(), expected.parameters.size()); EXPECT_EQ(got.parameters.size(), expected.parameters.size());
...@@ -68,7 +59,7 @@ void ParseAndCheck(const char* input, const ProperyNodeCheck& expected) { ...@@ -68,7 +59,7 @@ void ParseAndCheck(const char* input, const ProperyNodeCheck& expected) {
Check(Parse(input), expected); Check(Parse(input), expected);
} }
TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) { TEST_F(AXPropertyNodeTest, ParseProperty) {
// Properties and methods. // Properties and methods.
ParseAndCheck("Role", "Role"); ParseAndCheck("Role", "Role");
ParseAndCheck("ChildAt(3)", "ChildAt(3)"); ParseAndCheck("ChildAt(3)", "ChildAt(3)");
...@@ -151,4 +142,4 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) { ...@@ -151,4 +142,4 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) {
"anchor: {}(:2, 1, down)"); "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