Commit 0e1249fe authored by Alexander Surkov's avatar Alexander Surkov Committed by Commit Bot

DumpAccTree testing: implement nested attribute calls on mac

For example, AXIndexForTextMarker(AXTextMarkerForIndex(0)) which gets a text marker by index and converts it to the index back

Bug: 1100991
Change-Id: I961190dcb4e9aab3c0a6302b4f5ea3df8b5ca4a4
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2346744
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796683}
parent a5e69667
...@@ -70,6 +70,10 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) { ...@@ -70,6 +70,10 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) {
ParseAndCheck("Text({dict: [1, 2]})", "Text({}(dict: [](1, 2)))"); ParseAndCheck("Text({dict: [1, 2]})", "Text({}(dict: [](1, 2)))");
ParseAndCheck("Text({dict: ValueFor(1)})", "Text({}(dict: ValueFor(1)))"); ParseAndCheck("Text({dict: ValueFor(1)})", "Text({}(dict: ValueFor(1)))");
// Nested arguments
ParseAndCheck("AXIndexForTextMarker(AXTextMarkerForIndex(0))",
"AXIndexForTextMarker(AXTextMarkerForIndex(0))");
// Line indexes filter. // Line indexes filter.
ParseAndCheck(":3,:5;AXDOMClassList", ":3,:5;AXDOMClassList"); ParseAndCheck(":3,:5;AXDOMClassList", ":3,:5;AXDOMClassList");
......
...@@ -133,7 +133,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, ...@@ -133,7 +133,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
} }
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
LineIndexFilter) { Filters_LineIndex) {
TestAndCheck(R"~~(data:text/html, TestAndCheck(R"~~(data:text/html,
<input class='input_at_3rd_line'> <input class='input_at_3rd_line'>
<input class='input_at_4th_line'> <input class='input_at_4th_line'>
...@@ -328,4 +328,15 @@ IN_PROC_BROWSER_TEST_F( ...@@ -328,4 +328,15 @@ IN_PROC_BROWSER_TEST_F(
)~~"); )~~");
} }
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
NestedCalls_Attributes) {
TestAndCheck(R"~~(data:text/html,
<p>Text</p>)~~",
{":1;AXIndexForTextMarker(AXTextMarkerForIndex(0))"},
R"~~(AXWebArea AXIndexForTextMarker(AXTextMarkerForIndex(0))=0
++AXGroup
++++AXStaticText AXValue='Text'
)~~");
}
} // namespace content } // namespace content
...@@ -19,28 +19,28 @@ namespace a11y { ...@@ -19,28 +19,28 @@ namespace a11y {
namespace { namespace {
#define INT_FAIL(property_node, msg) \ #define INT_FAIL(property_node, msg) \
LOG(ERROR) << "Failed to parse " << property_node.original_property \ LOG(ERROR) << "Failed to parse " << property_node.name_or_value \
<< " to Int: " << msg; \ << " to Int: " << msg; \
return nil; return nil;
#define INTARRAY_FAIL(property_node, msg) \ #define INTARRAY_FAIL(property_node, msg) \
LOG(ERROR) << "Failed to parse " << property_node.original_property \ LOG(ERROR) << "Failed to parse " << property_node.name_or_value \
<< " to IntArray: " << msg; \ << " to IntArray: " << msg; \
return nil; return nil;
#define NSRANGE_FAIL(property_node, msg) \ #define NSRANGE_FAIL(property_node, msg) \
LOG(ERROR) << "Failed to parse " << property_node.original_property \ LOG(ERROR) << "Failed to parse " << property_node.name_or_value \
<< " to NSRange: " << msg; \ << " to NSRange: " << msg; \
return nil; return nil;
#define UIELEMENT_FAIL(property_node, msg) \ #define UIELEMENT_FAIL(property_node, msg) \
LOG(ERROR) << "Failed to parse " << property_node.original_property \ LOG(ERROR) << "Failed to parse " << property_node.name_or_value \
<< " to UIElement: " << msg; \ << " to UIElement: " << msg; \
return nil; return nil;
#define TEXTMARKER_FAIL(property_node, msg) \ #define TEXTMARKER_FAIL(property_node, msg) \
LOG(ERROR) << "Failed to parse " << property_node.original_property \ LOG(ERROR) << "Failed to parse " << property_node.name_or_value \
<< " to AXTextMarker: " << msg \ << " to AXTextMarker: " << msg \
<< ". Expected format: {anchor, offset, affinity}, where anchor " \ << ". Expected format: {anchor, offset, affinity}, where anchor " \
"is :line_num, offset is integer, affinity is either down, " \ "is :line_num, offset is integer, affinity is either down, " \
...@@ -123,29 +123,42 @@ OptionalNSObject AttributeInvoker::Invoke( ...@@ -123,29 +123,42 @@ OptionalNSObject AttributeInvoker::Invoke(
OptionalNSObject AttributeInvoker::ParamByPropertyNode( OptionalNSObject AttributeInvoker::ParamByPropertyNode(
const PropertyNode& property_node) const { const PropertyNode& 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
<< ": single parameter is expected";
return OptionalNSObject::Error();
}
// Nested attribute case: attempt to invoke an attribute for an argument node.
const PropertyNode& arg_node = property_node.parameters[0];
OptionalNSObject subvalue = Invoke(arg_node);
if (!subvalue.IsNotApplicable()) {
return subvalue;
}
// Otherwise parse argument node value.
std::string property_name = base::UTF16ToASCII(property_node.name_or_value); std::string property_name = base::UTF16ToASCII(property_node.name_or_value);
if (property_name == "AXLineForIndex") { // Int if (property_name == "AXLineForIndex" ||
return OptionalNSObject::NotNilOrError(PropertyNodeToInt(property_node)); property_name == "AXTextMarkerForIndex") { // Int
return OptionalNSObject::NotNilOrError(PropertyNodeToInt(arg_node));
} }
if (property_name == "AXCellForColumnAndRow") { // IntArray if (property_name == "AXCellForColumnAndRow") { // IntArray
return OptionalNSObject::NotNilOrError( return OptionalNSObject::NotNilOrError(PropertyNodeToIntArray(arg_node));
PropertyNodeToIntArray(property_node));
} }
if (property_name == "AXStringForRange") { // NSRange if (property_name == "AXStringForRange") { // NSRange
return OptionalNSObject::NotNilOrError(PropertyNodeToRange(property_node)); return OptionalNSObject::NotNilOrError(PropertyNodeToRange(arg_node));
} }
if (property_name == "AXIndexForChildUIElement") { // UIElement if (property_name == "AXIndexForChildUIElement") { // UIElement
return OptionalNSObject::NotNilOrError( return OptionalNSObject::NotNilOrError(PropertyNodeToUIElement(arg_node));
PropertyNodeToUIElement(property_node));
} }
if (property_name == "AXIndexForTextMarker") { // TextMarker if (property_name == "AXIndexForTextMarker") { // TextMarker
return OptionalNSObject::NotNilOrError( return OptionalNSObject::NotNilOrError(PropertyNodeToTextMarker(arg_node));
PropertyNodeToTextMarker(property_node));
} }
if (property_name == "AXStringForTextMarkerRange") { // TextMarkerRange if (property_name == "AXStringForTextMarkerRange") { // TextMarkerRange
return OptionalNSObject::NotNilOrError( return OptionalNSObject::NotNilOrError(
PropertyNodeToTextMarkerRange(property_node)); PropertyNodeToTextMarkerRange(arg_node));
} }
return OptionalNSObject::NotApplicable(); return OptionalNSObject::NotApplicable();
...@@ -153,29 +166,19 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode( ...@@ -153,29 +166,19 @@ OptionalNSObject AttributeInvoker::ParamByPropertyNode(
// NSNumber. Format: integer. // NSNumber. Format: integer.
NSNumber* AttributeInvoker::PropertyNodeToInt( NSNumber* AttributeInvoker::PropertyNodeToInt(
const PropertyNode& property_node) const { const PropertyNode& intnode) const {
if (property_node.parameters.size() != 1) {
INT_FAIL(property_node, "single argument is expected")
}
const auto& intnode = property_node.parameters[0];
base::Optional<int> param = intnode.AsInt(); base::Optional<int> param = intnode.AsInt();
if (!param) { if (!param) {
INT_FAIL(property_node, "not a number") INT_FAIL(intnode, "not a number")
} }
return [NSNumber numberWithInt:*param]; return [NSNumber numberWithInt:*param];
} }
// NSArray of two NSNumber. Format: [integer, integer]. // NSArray of two NSNumber. Format: [integer, integer].
NSArray* AttributeInvoker::PropertyNodeToIntArray( NSArray* AttributeInvoker::PropertyNodeToIntArray(
const PropertyNode& property_node) const { const PropertyNode& arraynode) const {
if (property_node.parameters.size() != 1) {
INTARRAY_FAIL(property_node, "single argument is expected")
}
const auto& arraynode = property_node.parameters[0];
if (arraynode.name_or_value != base::ASCIIToUTF16("[]")) { if (arraynode.name_or_value != base::ASCIIToUTF16("[]")) {
INTARRAY_FAIL(property_node, "not array") INTARRAY_FAIL(arraynode, "not array")
} }
NSMutableArray* array = NSMutableArray* array =
...@@ -183,8 +186,8 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray( ...@@ -183,8 +186,8 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray(
for (const auto& paramnode : arraynode.parameters) { for (const auto& paramnode : arraynode.parameters) {
base::Optional<int> param = paramnode.AsInt(); base::Optional<int> param = paramnode.AsInt();
if (!param) { if (!param) {
INTARRAY_FAIL(property_node, paramnode.name_or_value + INTARRAY_FAIL(arraynode, paramnode.name_or_value +
base::UTF8ToUTF16(" is not a number")) base::UTF8ToUTF16(" is not a number"))
} }
[array addObject:@(*param)]; [array addObject:@(*param)];
} }
...@@ -193,24 +196,19 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray( ...@@ -193,24 +196,19 @@ NSArray* AttributeInvoker::PropertyNodeToIntArray(
// NSRange. Format: {loc: integer, len: integer}. // NSRange. Format: {loc: integer, len: integer}.
NSValue* AttributeInvoker::PropertyNodeToRange( NSValue* AttributeInvoker::PropertyNodeToRange(
const PropertyNode& property_node) const { const PropertyNode& dictnode) const {
if (property_node.parameters.size() != 1) {
NSRANGE_FAIL(property_node, "single argument is expected")
}
const auto& dictnode = property_node.parameters[0];
if (!dictnode.IsDict()) { if (!dictnode.IsDict()) {
NSRANGE_FAIL(property_node, "dictionary is expected") NSRANGE_FAIL(dictnode, "dictionary is expected")
} }
base::Optional<int> loc = dictnode.FindIntKey("loc"); base::Optional<int> loc = dictnode.FindIntKey("loc");
if (!loc) { if (!loc) {
NSRANGE_FAIL(property_node, "no loc or loc is not a number") NSRANGE_FAIL(dictnode, "no loc or loc is not a number")
} }
base::Optional<int> len = dictnode.FindIntKey("len"); base::Optional<int> len = dictnode.FindIntKey("len");
if (!len) { if (!len) {
NSRANGE_FAIL(property_node, "no len or len is not a number") NSRANGE_FAIL(dictnode, "no len or len is not a number")
} }
return [NSValue valueWithRange:NSMakeRange(*loc, *len)]; return [NSValue valueWithRange:NSMakeRange(*loc, *len)];
...@@ -218,15 +216,11 @@ NSValue* AttributeInvoker::PropertyNodeToRange( ...@@ -218,15 +216,11 @@ NSValue* AttributeInvoker::PropertyNodeToRange(
// UIElement. Format: :line_num. // UIElement. Format: :line_num.
gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement( gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement(
const PropertyNode& property_node) const { const PropertyNode& uielement_node) const {
if (property_node.parameters.size() != 1) { gfx::NativeViewAccessible uielement =
UIELEMENT_FAIL(property_node, "single argument is expected") line_indexes_map.NodeBy(base::UTF16ToUTF8(uielement_node.name_or_value));
}
gfx::NativeViewAccessible uielement = line_indexes_map.NodeBy(
base::UTF16ToUTF8(property_node.parameters[0].name_or_value));
if (!uielement) { if (!uielement) {
UIELEMENT_FAIL(property_node, UIELEMENT_FAIL(uielement_node,
"no corresponding UIElement was found in the tree") "no corresponding UIElement was found in the tree")
} }
return uielement; return uielement;
...@@ -267,42 +261,34 @@ id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const { ...@@ -267,42 +261,34 @@ id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const {
} }
id AttributeInvoker::PropertyNodeToTextMarker( id AttributeInvoker::PropertyNodeToTextMarker(
const PropertyNode& property_node) const { const PropertyNode& dictnode) const {
if (property_node.parameters.size() != 1) { return DictNodeToTextMarker(dictnode);
TEXTMARKER_FAIL(property_node, "single argument is expected")
}
return DictNodeToTextMarker(property_node.parameters[0]);
} }
id AttributeInvoker::PropertyNodeToTextMarkerRange( id AttributeInvoker::PropertyNodeToTextMarkerRange(
const PropertyNode& property_node) const { const PropertyNode& rangenode) const {
if (property_node.parameters.size() != 1) {
TEXTMARKER_FAIL(property_node, "single argument is expected")
}
const auto& rangenode = property_node.parameters[0];
if (!rangenode.IsDict()) { if (!rangenode.IsDict()) {
TEXTMARKER_FAIL(property_node, "dictionary is expected") TEXTMARKER_FAIL(rangenode, "dictionary is expected")
} }
const PropertyNode* anchornode = rangenode.FindKey("anchor"); const PropertyNode* anchornode = rangenode.FindKey("anchor");
if (!anchornode) { if (!anchornode) {
TEXTMARKER_FAIL(property_node, "no anchor") TEXTMARKER_FAIL(rangenode, "no anchor")
} }
id anchor_textmarker = DictNodeToTextMarker(*anchornode); id anchor_textmarker = DictNodeToTextMarker(*anchornode);
if (!anchor_textmarker) { if (!anchor_textmarker) {
TEXTMARKER_FAIL(property_node, "failed to parse anchor") TEXTMARKER_FAIL(rangenode, "failed to parse anchor")
} }
const PropertyNode* focusnode = rangenode.FindKey("focus"); const PropertyNode* focusnode = rangenode.FindKey("focus");
if (!focusnode) { if (!focusnode) {
TEXTMARKER_FAIL(property_node, "no focus") TEXTMARKER_FAIL(rangenode, "no focus")
} }
id focus_textmarker = DictNodeToTextMarker(*focusnode); id focus_textmarker = DictNodeToTextMarker(*focusnode);
if (!focus_textmarker) { if (!focus_textmarker) {
TEXTMARKER_FAIL(property_node, "failed to parse focus") TEXTMARKER_FAIL(rangenode, "failed to parse focus")
} }
return content::AXTextMarkerRangeFrom(anchor_textmarker, focus_textmarker); return content::AXTextMarkerRangeFrom(anchor_textmarker, focus_textmarker);
......
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