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

dumpacctree testsuite: implement scripting on mac

Bug: 1136957
Change-Id: I7ccfadcac9f584668508234b3869e919afece962
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2563923Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Cr-Commit-Position: refs/heads/master@{#835752}
parent cd633fdb
......@@ -33,8 +33,13 @@ class CONTENT_EXPORT AccessibilityTreeFormatterMac
std::vector<ui::AXPropertyFilter>* property_filters) override;
private:
base::Value BuildTree(const id root) const;
base::Value BuildTreeForAXUIElement(AXUIElementRef node) const;
// Runs all scripts defined by given property filters.
void EvaluateScripts(const a11y::LineIndexer* line_indexer,
base::Value* dict) const;
void RecursiveBuildTree(const id node,
const a11y::LineIndexer* line_indexer,
base::Value* dict) const;
......
......@@ -81,12 +81,9 @@ void AccessibilityTreeFormatterMac::AddDefaultFilters(
base::Value AccessibilityTreeFormatterMac::BuildTree(
ui::AXPlatformNodeDelegate* root) const {
DCHECK(root);
BrowserAccessibilityCocoa* cocoa_root = ToBrowserAccessibilityCocoa(
BrowserAccessibility::FromAXPlatformNodeDelegate(root));
LineIndexer line_indexer(cocoa_root);
base::Value dict(base::Value::Type::DICTIONARY);
RecursiveBuildTree(cocoa_root, &line_indexer, &dict);
return dict;
BrowserAccessibility* internal_root =
BrowserAccessibility::FromAXPlatformNodeDelegate(root);
return BuildTree(ToBrowserAccessibilityCocoa(internal_root));
}
base::Value AccessibilityTreeFormatterMac::BuildTreeForWindow(
......@@ -106,13 +103,44 @@ base::Value AccessibilityTreeFormatterMac::BuildTreeForSelector(
base::Value AccessibilityTreeFormatterMac::BuildTreeForAXUIElement(
AXUIElementRef node) const {
LineIndexer line_indexer(static_cast<id>(node));
return BuildTree(static_cast<id>(node));
}
base::Value AccessibilityTreeFormatterMac::BuildTree(const id root) const {
DCHECK(root);
LineIndexer line_indexer(root);
base::Value dict(base::Value::Type::DICTIONARY);
RecursiveBuildTree(static_cast<id>(node), &line_indexer, &dict);
EvaluateScripts(&line_indexer, &dict);
RecursiveBuildTree(root, &line_indexer, &dict);
return dict;
}
void AccessibilityTreeFormatterMac::EvaluateScripts(
const LineIndexer* line_indexer,
base::Value* dict) const {
base::Value scripts(base::Value::Type::LIST);
for (const AXPropertyNode& property_node : ScriptPropertyNodes()) {
AttributeInvoker invoker(line_indexer);
OptionalNSObject value = invoker.Invoke(property_node);
if (value.IsNotApplicable()) {
continue;
}
std::string result;
if (value.IsError()) {
result = kFailedToParseArgsError;
} else {
result = FormatAttributeValue(PopulateObject(*value, line_indexer));
}
std::string code = property_node.original_property;
scripts.Append(code + "=" + result);
}
dict->SetPath(kScriptsDictAttr, std::move(scripts));
}
void AccessibilityTreeFormatterMac::RecursiveBuildTree(
const id node,
const LineIndexer* line_indexer,
......
......@@ -22,12 +22,21 @@ namespace content {
namespace {
const ui::AXPropertyFilter::Type ALLOW_EMPTY =
ui::AXPropertyFilter::ALLOW_EMPTY;
const ui::AXPropertyFilter::Type SCRIPT = ui::AXPropertyFilter::SCRIPT;
class AccessibilityTreeFormatterMacBrowserTest : public ContentBrowserTest {
public:
AccessibilityTreeFormatterMacBrowserTest() {}
~AccessibilityTreeFormatterMacBrowserTest() override {}
// Checks the formatted accessible tree for the given data URL.
void TestAndCheck(const char* url,
const std::vector<ui::AXPropertyFilter>& property_filters,
const std::vector<ui::AXNodeFilter>& node_filters,
const char* expected) const;
void TestAndCheck(const char* url,
const std::vector<const char*>& filters,
const char* expected) const;
......@@ -48,7 +57,8 @@ class AccessibilityTreeFormatterMacBrowserTest : public ContentBrowserTest {
void AccessibilityTreeFormatterMacBrowserTest::TestAndCheck(
const char* url,
const std::vector<const char*>& filters,
const std::vector<ui::AXPropertyFilter>& property_filters,
const std::vector<ui::AXNodeFilter>& node_filters,
const char* expected) const {
ASSERT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
......@@ -61,15 +71,9 @@ void AccessibilityTreeFormatterMacBrowserTest::TestAndCheck(
std::unique_ptr<ui::AXTreeFormatter> formatter =
AXInspectFactory::CreatePlatformFormatter();
std::vector<ui::AXPropertyFilter> property_filters;
for (const char* filter : filters) {
property_filters.push_back(
ui::AXPropertyFilter(filter, ui::AXPropertyFilter::ALLOW_EMPTY));
}
formatter->SetPropertyFilters(property_filters,
ui::AXTreeFormatter::kFiltersDefaultSet);
formatter->SetNodeFilters(node_filters);
BrowserAccessibility* root = GetManager()->GetRoot();
ASSERT_NE(nullptr, root);
......@@ -78,6 +82,17 @@ void AccessibilityTreeFormatterMacBrowserTest::TestAndCheck(
EXPECT_EQ(actual, expected);
}
void AccessibilityTreeFormatterMacBrowserTest::TestAndCheck(
const char* url,
const std::vector<const char*>& filters,
const char* expected) const {
std::vector<ui::AXPropertyFilter> property_filters;
for (const char* filter : filters) {
property_filters.emplace_back(filter, ALLOW_EMPTY);
}
TestAndCheck(url, property_filters, {}, expected);
}
void AccessibilityTreeFormatterMacBrowserTest::TestWrongParameters(
const char* url,
const std::vector<const char*>& parameters,
......@@ -330,8 +345,7 @@ IN_PROC_BROWSER_TEST_F(
)~~");
}
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
NestedCalls_Attributes) {
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, NestedCalls) {
TestAndCheck(R"~~(data:text/html,
<p>Text</p>)~~",
{":1;AXIndexForTextMarker(AXTextMarkerForIndex(0))"},
......@@ -341,4 +355,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest,
)~~");
}
IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, Script) {
TestAndCheck(R"~~(data:text/html,
<input aria-label='input'>)~~",
{{":3.AXRole", SCRIPT}}, {{"*", "*"}},
R"~~(:3.AXRole='AXTextField'
)~~");
}
} // namespace content
......@@ -69,6 +69,7 @@ class CONTENT_EXPORT OptionalNSObject final {
// Invokes attributes matching the given property filter.
class CONTENT_EXPORT AttributeInvoker final {
public:
AttributeInvoker(const LineIndexer* line_indexer);
AttributeInvoker(const id node, const LineIndexer* line_indexer);
// Invokes an attribute matching to a property filter.
......@@ -83,6 +84,9 @@ class CONTENT_EXPORT AttributeInvoker final {
const OptionalNSObject& value) const;
private:
// Returns an accessible object of the given property node or default one.
id TargetOf(const ui::AXPropertyNode& property_node) const;
// Returns a parameterized attribute parameter by a property node.
OptionalNSObject ParamByPropertyNode(const ui::AXPropertyNode&) const;
......@@ -101,8 +105,6 @@ class CONTENT_EXPORT AttributeInvoker final {
const id node;
const LineIndexer* line_indexer;
const NSArray* attributes;
const NSArray* parameterized_attributes;
};
// bindings
......
......@@ -109,30 +109,33 @@ std::string OptionalNSObject::ToString() const {
// AttributeInvoker
AttributeInvoker::AttributeInvoker(const LineIndexer* line_indexer)
: node(nullptr), line_indexer(line_indexer) {}
AttributeInvoker::AttributeInvoker(const id node,
const LineIndexer* line_indexer)
: node(node), line_indexer(line_indexer) {
attributes = AttributeNamesOf(node);
parameterized_attributes = ParameterizedAttributeNamesOf(node);
}
OptionalNSObject AttributeInvoker::Invoke(
const AXPropertyNode& property_node) const {
id target = TargetOf(property_node);
// Attributes
for (NSString* attribute : attributes) {
for (NSString* attribute : AttributeNamesOf(target)) {
if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
return OptionalNSObject::NotNullOrNotApplicable(
AttributeValueOf(node, attribute));
AttributeValueOf(target, attribute));
}
}
// Parameterized attributes
for (NSString* attribute : parameterized_attributes) {
for (NSString* attribute : ParameterizedAttributeNamesOf(target)) {
if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
OptionalNSObject param = ParamByPropertyNode(property_node);
if (param.IsNotNil()) {
return OptionalNSObject(
ParameterizedAttributeValueOf(node, attribute, *param));
ParameterizedAttributeValueOf(target, attribute, *param));
}
return param;
}
......@@ -145,7 +148,8 @@ OptionalNSObject AttributeInvoker::GetValue(
const std::string& property_name,
const OptionalNSObject& param) const {
NSString* attribute = base::SysUTF8ToNSString(property_name);
if ([parameterized_attributes containsObject:attribute]) {
NSArray* attributes = ParameterizedAttributeNamesOf(node);
if ([attributes containsObject:attribute]) {
if (param.IsNotNil()) {
return OptionalNSObject(
ParameterizedAttributeValueOf(node, attribute, *param));
......@@ -159,7 +163,8 @@ OptionalNSObject AttributeInvoker::GetValue(
OptionalNSObject AttributeInvoker::GetValue(
const std::string& property_name) const {
NSString* attribute = base::SysUTF8ToNSString(property_name);
if ([attributes containsObject:attribute]) {
NSArray* parameterized_attributes = AttributeNamesOf(node);
if ([parameterized_attributes containsObject:attribute]) {
return OptionalNSObject::NotNullOrNotApplicable(
AttributeValueOf(node, attribute));
}
......@@ -169,12 +174,19 @@ OptionalNSObject AttributeInvoker::GetValue(
void AttributeInvoker::SetValue(const std::string& property_name,
const OptionalNSObject& value) const {
NSString* attribute = base::SysUTF8ToNSString(property_name);
NSArray* attributes = AttributeNamesOf(node);
if ([attributes containsObject:attribute] &&
IsAttributeSettable(node, attribute)) {
SetAttributeValueOf(node, attribute, *value);
}
}
id AttributeInvoker::TargetOf(const AXPropertyNode& property_node) const {
return property_node.target.empty()
? node
: line_indexer->NodeBy(property_node.target);
}
OptionalNSObject AttributeInvoker::ParamByPropertyNode(
const AXPropertyNode& property_node) const {
// NSAccessibility attributes always take a single parameter.
......
......@@ -51,7 +51,7 @@ struct AX_EXPORT AXTreeSelector {
// DumpAccessibilityTestBase::ParseHtmlForExtraDirectives() for more
// information.
struct AX_EXPORT AXPropertyFilter {
enum Type { ALLOW, ALLOW_EMPTY, DENY };
enum Type { ALLOW, ALLOW_EMPTY, DENY, SCRIPT };
std::string match_str;
std::string property_str;
......
......@@ -33,6 +33,7 @@ bool AXTreeFormatter::MatchesPropertyFilters(
base::MatchPattern(text, filter.match_str + "=*"))) {
switch (filter.type) {
case AXPropertyFilter::ALLOW_EMPTY:
case AXPropertyFilter::SCRIPT:
allow = true;
break;
case AXPropertyFilter::ALLOW:
......@@ -52,6 +53,9 @@ bool AXTreeFormatter::MatchesNodeFilters(
const std::vector<AXNodeFilter>& node_filters,
const base::Value& dict) {
for (const auto& filter : node_filters) {
if (filter.property == "*") {
return true;
}
const std::string* value = dict.FindStringKey(filter.property);
if (value && base::MatchPattern(*value, filter.pattern)) {
return true;
......
......@@ -25,6 +25,7 @@ AXTreeFormatterBase::~AXTreeFormatterBase() = default;
// static
const char AXTreeFormatterBase::kChildrenDictAttr[] = "children";
const char AXTreeFormatterBase::kScriptsDictAttr[] = "scripts";
std::string AXTreeFormatterBase::Format(AXPlatformNodeDelegate* root) const {
DCHECK(root);
......@@ -33,7 +34,20 @@ std::string AXTreeFormatterBase::Format(AXPlatformNodeDelegate* root) const {
std::string AXTreeFormatterBase::FormatTree(const base::Value& dict) const {
std::string contents;
// Format the tree.
RecursiveFormatTree(dict, &contents);
// Format scripts.
const base::Value* scripts = dict.FindListKey(kScriptsDictAttr);
if (!scripts)
return contents;
for (const base::Value& script : scripts->GetList()) {
WriteAttribute(false, script.GetString(), &contents);
contents += "\n";
}
return contents;
}
......@@ -108,6 +122,7 @@ std::vector<AXPropertyNode> AXTreeFormatterBase::PropertyFilterNodesFor(
case AXPropertyFilter::ALLOW:
list.push_back(std::move(property_node));
break;
case AXPropertyFilter::SCRIPT:
case AXPropertyFilter::DENY:
break;
default:
......@@ -117,6 +132,16 @@ std::vector<AXPropertyNode> AXTreeFormatterBase::PropertyFilterNodesFor(
return list;
}
std::vector<AXPropertyNode> AXTreeFormatterBase::ScriptPropertyNodes() const {
std::vector<AXPropertyNode> list;
for (const auto& filter : property_filters_) {
if (filter.type == AXPropertyFilter::SCRIPT) {
list.push_back(AXPropertyNode::From(filter));
}
}
return list;
}
bool AXTreeFormatterBase::HasMatchAllPropertyFilter() const {
for (const auto& filter : property_filters_) {
if (filter.type == AXPropertyFilter::ALLOW && filter.match_str == "*") {
......
......@@ -38,6 +38,7 @@ class AX_EXPORT AXTreeFormatterBase : public AXTreeFormatter {
protected:
static const char kChildrenDictAttr[];
static const char kScriptsDictAttr[];
//
// Overridden by platform subclasses.
......@@ -52,6 +53,9 @@ class AX_EXPORT AXTreeFormatterBase : public AXTreeFormatter {
std::vector<AXPropertyNode> PropertyFilterNodesFor(
const std::string& line_index) const;
// Returns a list of script property nodes.
std::vector<ui::AXPropertyNode> ScriptPropertyNodes() const;
// Return true if match-all filter is present.
bool HasMatchAllPropertyFilter() const;
......
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