Commit aad94f9f authored by Alexander Surkov's avatar Alexander Surkov Committed by Chromium LUCI CQ

a11y inspect: allow DOM ids as accessible object references for

inspect's scripting

Support inspect property filters such as a textbox.AXRole, where
|textbox| is DOM id attribute of an HTML element which allows to
create easy-to-read scripting scenarios.

Bug: 1136957
Change-Id: Ia69466c9692bd2fe385cfa23a3224df22f215a90
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2586117
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840411}
parent ee22256e
......@@ -51,7 +51,7 @@ const char kRangeLenDictAttr[] = "len";
const char kSetKeyPrefixDictAttr[] = "_setkey_";
const char kConstValuePrefix[] = "_const_";
const char kNULLValue[] = "_const_NULL";
const char kFailedToParseArgsError[] = "_const_ERROR:FAILED_TO_PARSE_ARGS";
const char kFailedToParseError[] = "_const_ERROR:FAILED_TO_PARSE";
} // namespace
......@@ -126,14 +126,11 @@ void AccessibilityTreeFormatterMac::EvaluateScripts(
continue;
}
std::string result;
if (value.IsError()) {
result = kFailedToParseArgsError;
} else {
result = FormatAttributeValue(PopulateObject(*value, line_indexer));
}
base::Value result = value.IsError() ? base::Value(kFailedToParseError)
: PopulateObject(*value, line_indexer);
std::string code = property_node.original_property;
scripts.Append(code + "=" + result);
scripts.Append(code + "=" + FormatAttributeValue(result));
}
dict->SetPath(kScriptsDictAttr, std::move(scripts));
}
......@@ -188,7 +185,7 @@ void AccessibilityTreeFormatterMac::AddProperties(
}
if (value.IsError()) {
dict->SetPath(property_node.original_property,
base::Value(kFailedToParseArgsError));
base::Value(kFailedToParseError));
continue;
}
dict->SetPath(property_node.original_property,
......
......@@ -193,7 +193,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
<p contentEditable='true'>Text</p>)~~",
{"1, 2", "NaN"}, ":2;AXLineForIndex(Argument)=*",
R"~~(AXWebArea
++AXTextArea AXLineForIndex(Argument)=ERROR:FAILED_TO_PARSE_ARGS AXValue='Text'
++AXTextArea AXLineForIndex(Argument)=ERROR:FAILED_TO_PARSE AXValue='Text'
++++AXStaticText AXValue='Text'
)~~");
}
......@@ -239,7 +239,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
<table role="grid"><tr><td>CELL</td></tr></table>)~~",
{"0, 0", "{1, 2}", "[1, NaN]", "[NaN, 1]"},
"AXCellForColumnAndRow(Argument)=*", R"~~(AXWebArea
++AXTable AXCellForColumnAndRow(Argument)=ERROR:FAILED_TO_PARSE_ARGS
++AXTable AXCellForColumnAndRow(Argument)=ERROR:FAILED_TO_PARSE
++++AXRow
++++++AXCell
++++++++AXStaticText AXValue='CELL'
......@@ -267,7 +267,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
{"1, 2", "[]", "{loc: 1, leno: 2}", "{loco: 1, len: 2}",
"{loc: NaN, len: 2}", "{loc: 2, len: NaN}"},
":2;AXStringForRange(Argument)=*", R"~~(AXWebArea
++AXTextArea AXStringForRange(Argument)=ERROR:FAILED_TO_PARSE_ARGS AXValue='Text'
++AXTextArea AXStringForRange(Argument)=ERROR:FAILED_TO_PARSE AXValue='Text'
++++AXStaticText AXValue='Text'
)~~");
}
......@@ -289,7 +289,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
{"1, 2", "2", ":4"},
":2;AXIndexForChildUIElement(Argument)=*",
R"~~(AXWebArea
++AXTextArea AXIndexForChildUIElement(Argument)=ERROR:FAILED_TO_PARSE_ARGS AXValue='Text'
++AXTextArea AXIndexForChildUIElement(Argument)=ERROR:FAILED_TO_PARSE AXValue='Text'
++++AXStaticText AXValue='Text'
)~~");
}
......@@ -312,7 +312,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
<p>Text</p>)~~",
{"1, 2", "2", "{2, 1, down}", "{:2, NaN, down}", "{:2, 1, hoho}"},
":1;AXIndexForTextMarker(Argument)=*",
R"~~(AXWebArea AXIndexForTextMarker(Argument)=ERROR:FAILED_TO_PARSE_ARGS
R"~~(AXWebArea AXIndexForTextMarker(Argument)=ERROR:FAILED_TO_PARSE
++AXGroup
++++AXStaticText AXValue='Text'
)~~");
......@@ -339,7 +339,7 @@ IN_PROC_BROWSER_TEST_F(
{"1, 2", "2", "{focus: {:2, 1, down}}", "{anchor: {:2, 1, down}}",
"{anchor: {2, 1, down}, focus: {2, 1, down}}"},
":1;AXStringForTextMarkerRange(Argument)=*",
R"~~(AXWebArea AXStringForTextMarkerRange(Argument)=ERROR:FAILED_TO_PARSE_ARGS
R"~~(AXWebArea AXStringForTextMarkerRange(Argument)=ERROR:FAILED_TO_PARSE
++AXGroup
++++AXStaticText AXValue='Text'
)~~");
......@@ -363,4 +363,22 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, Script) {
)~~");
}
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
Script_ByDOMId) {
TestAndCheck(R"~~(data:text/html,
<input id='textbox' aria-label='input'>)~~",
{{"textbox.AXRole", SCRIPT}}, {{"*", "*"}},
R"~~(textbox.AXRole='AXTextField'
)~~");
}
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
Script_ByDOMId_WrongDOMId) {
TestAndCheck(R"~~(data:text/html,
<input id='textbox' aria-label='input'>)~~",
{{"textbo.AXRole", SCRIPT}}, {{"*", "*"}},
R"~~(textbo.AXRole=ERROR:FAILED_TO_PARSE
)~~");
}
} // namespace content
......@@ -29,7 +29,15 @@ class CONTENT_EXPORT LineIndexer final {
private:
void Build(const gfx::NativeViewAccessible node, int* counter);
std::map<const gfx::NativeViewAccessible, std::string> map;
struct NodeIdentifier {
std::string line_index;
std::string DOMid;
};
// Map between accessible objects and their identificators which can be a line
// index the object is placed at in an accessible tree or its DOM id
// attribute.
std::map<const gfx::NativeViewAccessible, NodeIdentifier> map;
};
// Implements stateful id values. Can be either id or be in
......
......@@ -60,12 +60,12 @@ std::string LineIndexer::IndexBy(const gfx::NativeViewAccessible node) const {
if (IsBrowserAccessibilityCocoa(node)) {
auto iter = map.find(node);
if (iter != map.end()) {
line_index = iter->second;
line_index = iter->second.line_index;
}
} else if (IsAXUIElement(node)) {
for (auto& iter : map) {
if (CFEqual(iter.first, node)) {
line_index = iter.second;
line_index = iter.second.line_index;
break;
}
}
......@@ -74,9 +74,12 @@ std::string LineIndexer::IndexBy(const gfx::NativeViewAccessible node) const {
}
gfx::NativeViewAccessible LineIndexer::NodeBy(
const std::string& line_index) const {
for (std::pair<const gfx::NativeViewAccessible, std::string> item : map) {
if (item.second == line_index) {
const std::string& identifier) const {
// Finds a first match either by a line number in :LINE_NUM format or by DOM
// id.
for (auto& item : map) {
if (item.second.line_index == identifier ||
item.second.DOMid == identifier) {
return item.first;
}
}
......@@ -86,7 +89,13 @@ gfx::NativeViewAccessible LineIndexer::NodeBy(
void LineIndexer::Build(const gfx::NativeViewAccessible node, int* counter) {
const std::string line_index =
std::string(1, ':') + base::NumberToString(++(*counter));
map.insert({node, line_index});
const id domid_value =
AttributeValueOf(node, base::SysUTF8ToNSString("AXDOMIdentifier"));
const std::string domid =
base::SysNSStringToUTF8(static_cast<NSString*>(domid_value));
map.insert({node, {line_index, domid}});
NSArray* children = ChildrenOf(node);
for (gfx::NativeViewAccessible child in children) {
Build(child, counter);
......@@ -120,6 +129,13 @@ AttributeInvoker::AttributeInvoker(const id node,
OptionalNSObject AttributeInvoker::Invoke(
const AXPropertyNode& property_node) const {
id target = TargetOf(property_node);
if (!target) {
// TODO(alexs): failing the tests when filters are incorrect is a good idea,
// however crashing ax_dump tools on wrong input might be not. Figure out
// a working solution that works nicely in both cases.
LOG(ERROR) << "No target to invoke attribute";
return OptionalNSObject::Error();
}
// Attributes
for (NSString* attribute : AttributeNamesOf(target)) {
......
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